Date: Sat, 10 Feb 2018 20:29:58 +0100 From: Eric Masson <emss@free.fr> To: freebsd-hackers@FreeBSD.org Subject: Regression between 10 & 11, Freeswitch hangs when exiting Message-ID: <86tvuor6bt.fsf@newsrv.interne.associated-bears.org>
index | next in thread | raw e-mail
[-- Attachment #1 --] Hi, Freeswitch, a multiprotocol softswitch (http://freewitch.org) runs on FreeBSD and a port is available (net/freeswitch/) Freeswitch runs fine on any 10.x installation, issueing the shutdown command from cli makes the application exit cleanly. On any 11.x installation, issueing the shutdown command from cli makes the application hang. FreeBSD version is : FreeBSD newsrv.interne.associated-bears.org 11.1-STABLE FreeBSD 11.1-STABLE #0 r327876M: Sat Jan 13 16:19:26 CET 2018 emss@newsrv.interne.associated-bears.org:/usr/obj/usr/src/sys/SE7525GP2 amd64 Generic or custom kernel behave the same. On Freeswitch side, 1.6.19 (net/freeswitch) and also git branches master & v1.6 show the same issue. I've reproduced the issue on the latest v1.6, the backtrace of stuck process shows the following : #0 0x00000008030ea91c in _umtx_op_err () from /lib/libthr.so.3 #1 0x00000008030e6f97 in join_common (pthread=0x807ed7700, thread_return=0x7fffffffb658, abstime=0x0) at /usr/src/lib/libthr/thread/thr_join.c:125 #2 0x0000000800f01421 in apr_thread_join (retval=0x7fffffffb68c, thd=0x8059e9658) at threadproc/unix/thread.c:234 #3 0x0000000800cd889e in do_shutdown (module=0x2, shutdown=<optimized out>, unload=SWITCH_TRUE, fail_if_busy=(unknown: 1523667750), err=<optimized out>) at src/switch_loadable_module.c:2031 #4 0x0000000800cdb84a in switch_loadable_module_shutdown () at src/switch_loadable_module.c:2083 #5 0x0000000800c9d92c in switch_core_destroy () at src/switch_core.c:2940 #6 0x0000000000403b2b in main (argc=<optimized out>, argv=0x7fffffffe988) at src/switch.c:1210 It seems the issue lies in thread handling. I've attached : - the output of a Freeswitch related script used to gather relevant information. - source files showing in bt Thank for help. Éric Masson -- Warning: file "/home/emss/misc/fortune/En_sig.dat" unreadable Warning: file "/home/emss/misc/fortune/Fr_sig.dat" unreadable Faut vraiment que je m'occupe de ce problème de signature :) [-- Attachment #2 --] GNU gdb (GDB) 8.0.1 [GDB v8.0.1 for FreeBSD] Copyright (C) 2017 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Type "show copying" and "show warranty" for details. This GDB was configured as "x86_64-portbld-freebsd11.1". Type "show configuration" for configuration details. For bug reporting instructions, please see: <http://www.gnu.org/software/gdb/bugs/>. Find the GDB manual and other documentation resources online at: <http://www.gnu.org/software/gdb/documentation/>. For help, type "help". Type "apropos word" to search for commands related to "word"... Reading symbols from /opt/freeswitch/bin/freeswitch...done. [New LWP 100834] [New LWP 101041] [New LWP 101042] [New LWP 101044] [New LWP 101045] [New LWP 101046] [New LWP 101047] [New LWP 101050] [New LWP 101051] [New LWP 101080] Core was generated by `/opt/freeswitch/bin/freeswitch -c'. #0 0x00000008030ea91c in _umtx_op_err () from /lib/libthr.so.3 [Current thread is 1 (LWP 100834)] (gdb) ================================================================================ # GDB session generated by FS backtrace-from-core # FreeSWITCH version: 1.9.0+git~20180202T143051Z~38153a37ed~64bit # FreeSWITCH version (human): 1.9.0 git 38153a3 2018-02-02 14:30:51Z 64bit ================================================================================ ================================================================================ # info threads ================================================================================ Id Target Id Frame * 1 LWP 100834 0x00000008030ea91c in _umtx_op_err () from /lib/libthr.so.3 2 LWP 101041 0x0000000803de665a in _select () from /lib/libc.so.7 3 LWP 101042 0x00000008030ea91c in _umtx_op_err () from /lib/libthr.so.3 4 LWP 101044 0x00000008030ea91c in _umtx_op_err () from /lib/libthr.so.3 5 LWP 101045 0x00000008030ea91c in _umtx_op_err () from /lib/libthr.so.3 6 LWP 101046 0x0000000803de665a in _select () from /lib/libc.so.7 7 LWP 101047 0x00000008030ea91c in _umtx_op_err () from /lib/libthr.so.3 8 LWP 101050 0x0000000803d7797a in _accept () from /lib/libc.so.7 9 LWP 101051 0x0000000803d7797a in _accept () from /lib/libc.so.7 10 LWP 101080 0x0000000803d7797a in _accept () from /lib/libc.so.7 ================================================================================ # bt ================================================================================ #0 0x00000008030ea91c in _umtx_op_err () from /lib/libthr.so.3 #1 0x00000008030e6f97 in join_common (pthread=0x807ed7700, thread_return=0x7fffffffb658, abstime=0x0) at /usr/src/lib/libthr/thread/thr_join.c:125 #2 0x0000000800f01421 in apr_thread_join (retval=0x7fffffffb68c, thd=0x8059e9658) at threadproc/unix/thread.c:234 #3 0x0000000800cd889e in do_shutdown (module=0x2, shutdown=<optimized out>, unload=SWITCH_TRUE, fail_if_busy=(unknown: 1523667750), err=<optimized out>) at src/switch_loadable_module.c:2031 #4 0x0000000800cdb84a in switch_loadable_module_shutdown () at src/switch_loadable_module.c:2083 #5 0x0000000800c9d92c in switch_core_destroy () at src/switch_core.c:2940 #6 0x0000000000403b2b in main (argc=<optimized out>, argv=0x7fffffffe988) at src/switch.c:1210 ================================================================================ # bt full ================================================================================ #0 0x00000008030ea91c in _umtx_op_err () from /lib/libthr.so.3 No symbol table info available. #1 0x00000008030e6f97 in join_common (pthread=0x807ed7700, thread_return=0x7fffffffb658, abstime=0x0) at /usr/src/lib/libthr/thread/thr_join.c:125 ret = <error reading variable ret (Cannot access memory at address 0x2d)> tid = 101080 ts = {tv_sec = 34411006815, tv_nsec = 34514890792} tsp = 0x18ad8 #2 0x0000000800f01421 in apr_thread_join (retval=0x7fffffffb68c, thd=0x8059e9658) at threadproc/unix/thread.c:234 stat = <optimized out> #3 0x0000000800cd889e in do_shutdown (module=0x2, shutdown=<optimized out>, unload=SWITCH_TRUE, fail_if_busy=(unknown: 1523667750), err=<optimized out>) at src/switch_loadable_module.c:2031 st = SWITCH_STATUS_SUCCESS pool = <optimized out> flags = 12798607 #4 0x0000000800cdb84a in switch_loadable_module_shutdown () at src/switch_loadable_module.c:2083 i = <error reading variable i (Cannot access memory at address 0x0)> hi = <optimized out> val = 0x805626460 module = 0x807ed7700 #5 0x0000000800c9d92c in switch_core_destroy () at src/switch_core.c:2940 event = 0x0 #6 0x0000000000403b2b in main (argc=<optimized out>, argv=0x7fffffffe988) at src/switch.c:1210 pid_path = "/opt/freeswitch/var/run/freeswitch/freeswitch.pid", '\000' <repeats 974 times> pid_buffer = "45848", '\000' <repeats 26 times> old_pid_buffer = "45690", '\000' <repeats 26 times> opts_str = '\000' <repeats 1023 times> local_argv = {0x7fffffffec40 "/opt/freeswitch/bin/freeswitch", 0x7fffffffec5f "-c", 0x0 <repeats 1022 times>} arg_argv = {0x0 <repeats 128 times>} local_argc = 2 reincarnate_reexec = <error reading variable reincarnate_reexec (Cannot access memory at address 0x0)> reincarnate = <error reading variable reincarnate (Cannot access memory at address 0x0)> do_wait = <error reading variable do_wait (Cannot access memory at address 0x0)> nf = <error reading variable nf (Cannot access memory at address 0x0)> pid = <optimized out> nc = <error reading variable nc (Cannot access memory at address 0x1)> ret = <error reading variable ret (Cannot access memory at address 0x0)> flags = 68225 priority = <error reading variable priority (Cannot access memory at address 0x0)> do_kill = 0 run_set = <error reading variable run_set (Cannot access memory at address 0x1)> log_set = 0 alt_base = <error reading variable alt_base (Cannot access memory at address 0x1)> alt_dirs = 0 x = <optimized out> opts = <optimized out> i = <optimized out> runas_group = 0x0 pid_len = 5 pool = <optimized out> fd = <optimized out> old_pid_len = 5 err = 0x800f28b86 "Success" destroy_status = <optimized out> ================================================================================ # thread apply all bt ================================================================================ Thread 10 (LWP 101080): #0 0x0000000803d7797a in _accept () from /lib/libc.so.7 #1 0x00000008030df2c6 in __thr_accept (s=67, addr=0x8088f5238, addrlen=0x8088f5218) at /usr/src/lib/libthr/thread/thr_syscalls.c:106 #2 0x0000000800eff5e0 in apr_socket_accept (new=0x7fffdec1aed8, sock=0x8088f31b0, connection_context=0x8088f5028) at network_io/unix/sockets.c:191 #3 0x000000080766bbb6 in mod_event_socket_runtime () at mod_event_socket.c:2987 #4 0x0000000800cd8da9 in switch_loadable_module_exec (thread=0x8059e9658, obj=0x8059e9238) at src/switch_loadable_module.c:113 #5 0x0000000800f013a6 in dummy_worker (opaque=0x8059e9658) at threadproc/unix/thread.c:151 #6 0x00000008030dcc05 in thread_start (curthread=0x807ed7700) at /usr/src/lib/libthr/thread/thr_create.c:289 #7 0x0000000000000000 in ?? () Backtrace stopped: Cannot access memory at address 0x7fffdec1b000 Thread 9 (LWP 101051): #0 0x0000000803d7797a in _accept () from /lib/libc.so.7 #1 0x00000008030df2c6 in __thr_accept (s=10, addr=0x806618238, addrlen=0x806618218) at /usr/src/lib/libthr/thread/thr_syscalls.c:106 #2 0x0000000800eff5e0 in apr_socket_accept (new=0x7fffdfa13f20, sock=0x8059de610, connection_context=0x806618028) at network_io/unix/sockets.c:191 #3 0x0000000800d5ae20 in msrp_listener (thread=<optimized out>, obj=0x8011be5e8 <globals+120>) at src/switch_msrp.c:1379 #4 0x0000000800f013a6 in dummy_worker (opaque=0x8059de838) at threadproc/unix/thread.c:151 #5 0x00000008030dcc05 in thread_start (curthread=0x804e19200) at /usr/src/lib/libthr/thread/thr_create.c:289 #6 0x0000000000000000 in ?? () Backtrace stopped: Cannot access memory at address 0x7fffdfa14000 Thread 8 (LWP 101050): #0 0x0000000803d7797a in _accept () from /lib/libc.so.7 #1 0x00000008030df2c6 in __thr_accept (s=8, addr=0x806018238, addrlen=0x806018218) at /usr/src/lib/libthr/thread/thr_syscalls.c:106 #2 0x0000000800eff5e0 in apr_socket_accept (new=0x7fffdfa50f20, sock=0x8059de1e8, connection_context=0x806018028) at network_io/unix/sockets.c:191 #3 0x0000000800d5ae20 in msrp_listener (thread=<optimized out>, obj=0x8011be5c8 <globals+88>) at src/switch_msrp.c:1379 #4 0x0000000800f013a6 in dummy_worker (opaque=0x8059de510) at threadproc/unix/thread.c:151 #5 0x00000008030dcc05 in thread_start (curthread=0x804e18d00) at /usr/src/lib/libthr/thread/thr_create.c:289 #6 0x0000000000000000 in ?? () Backtrace stopped: Cannot access memory at address 0x7fffdfa51000 Thread 7 (LWP 101047): #0 0x00000008030ea91c in _umtx_op_err () from /lib/libthr.so.3 #1 0x00000008030debb0 in _thr_umtx_timedwait_uint (mtx=0x80062e098, id=<optimized out>, clockid=<optimized out>, abstime=<optimized out>, shared=<optimized out>) at /usr/src/lib/libthr/thread/thr_umtx.c:234 #2 0x00000008030e8698 in cond_wait_user (abstime=<optimized out>, cancel=1, cvp=<optimized out>, mp=<optimized out>) at /usr/src/lib/libthr/thread/thr_cond.c:304 #3 cond_wait_common (cond=<optimized out>, mutex=<optimized out>, abstime=0x7fffdfe8fec8, cancel=1) at /usr/src/lib/libthr/thread/thr_cond.c:364 #4 0x0000000800efb3ef in apr_thread_cond_timedwait (cond=<optimized out>, mutex=<optimized out>, timeout=500000) at locks/unix/thread_cond.c:89 #5 0x0000000800ef16fb in apr_queue_pop_timeout (queue=0x8051dd158, data=0x7fffdfe8ff48, timeout=500000) at misc/apr_queue.c:339 #6 0x0000000800cd490b in switch_scheduler_task_thread (thread=<optimized out>, obj=<optimized out>) at src/switch_scheduler.c:188 #7 0x0000000800f013a6 in dummy_worker (opaque=0x805de85c8) at threadproc/unix/thread.c:151 #8 0x00000008030dcc05 in thread_start (curthread=0x804e18300) at /usr/src/lib/libthr/thread/thr_create.c:289 #9 0x0000000000000000 in ?? () Backtrace stopped: Cannot access memory at address 0x7fffdfe90000 Thread 6 (LWP 101046): #0 0x0000000803de665a in _select () from /lib/libc.so.7 #1 0x00000008030df9a2 in __thr_select (numfds=0, readfds=0x0, writefds=0x0, exceptfds=0x0, timeout=0x7fffdfeccf00) at /usr/src/lib/libthr/thread/thr_syscalls.c:493 #2 0x0000000800f02749 in apr_sleep (t=<optimized out>) at time/unix/time.c:246 #3 0x0000000800d4ce25 in do_sleep (t=0) at src/switch_time.c:164 #4 switch_sleep (t=0) at src/switch_time.c:632 #5 0x0000000800c86b8d in switch_core_sql_db_thread (thread=<optimized out>, obj=<optimized out>) at src/switch_core_sqldb.c:1395 #6 0x0000000800f013a6 in dummy_worker (opaque=0x805371c48) at threadproc/unix/thread.c:151 #7 0x00000008030dcc05 in thread_start (curthread=0x804e18800) at /usr/src/lib/libthr/thread/thr_create.c:289 #8 0x0000000000000000 in ?? () Backtrace stopped: Cannot access memory at address 0x7fffdfecd000 Thread 5 (LWP 101045): #0 0x00000008030ea91c in _umtx_op_err () from /lib/libthr.so.3 #1 0x00000008030debb0 in _thr_umtx_timedwait_uint (mtx=0x80062e068, id=<optimized out>, clockid=<optimized out>, abstime=<optimized out>, shared=<optimized out>) at /usr/src/lib/libthr/thread/thr_umtx.c:234 #2 0x00000008030e8698 in cond_wait_user (abstime=<optimized out>, cancel=1, cvp=<optimized out>, mp=<optimized out>) at /usr/src/lib/libthr/thread/thr_cond.c:304 #3 cond_wait_common (cond=<optimized out>, mutex=<optimized out>, abstime=0x0, cancel=1) at /usr/src/lib/libthr/thread/thr_cond.c:364 #4 0x0000000800c81e46 in switch_user_sql_thread (thread=<optimized out>, obj=0x8051fd0f0) at src/switch_core_sqldb.c:2135 #5 0x0000000800f013a6 in dummy_worker (opaque=0x805ad0880) at threadproc/unix/thread.c:151 #6 0x00000008030dcc05 in thread_start (curthread=0x804e17e00) at /usr/src/lib/libthr/thread/thr_create.c:289 #7 0x0000000000000000 in ?? () Backtrace stopped: Cannot access memory at address 0x7fffdff0a000 Thread 4 (LWP 101044): #0 0x00000008030ea91c in _umtx_op_err () from /lib/libthr.so.3 #1 0x00000008030debb0 in _thr_umtx_timedwait_uint (mtx=0x80062e050, id=<optimized out>, clockid=<optimized out>, abstime=<optimized out>, shared=<optimized out>) at /usr/src/lib/libthr/thread/thr_umtx.c:234 #2 0x00000008030e8698 in cond_wait_user (abstime=<optimized out>, cancel=1, cvp=<optimized out>, mp=<optimized out>) at /usr/src/lib/libthr/thread/thr_cond.c:304 #3 cond_wait_common (cond=<optimized out>, mutex=<optimized out>, abstime=0x0, cancel=1) at /usr/src/lib/libthr/thread/thr_cond.c:364 #4 0x0000000800ef1608 in apr_queue_pop (queue=0x805140908, data=0x7fffdff46f50) at misc/apr_queue.c:276 #5 0x0000000800d40a57 in log_thread (t=<optimized out>, obj=<optimized out>) at src/switch_log.c:294 #6 0x0000000800f013a6 in dummy_worker (opaque=0x805371b38) at threadproc/unix/thread.c:151 #7 0x00000008030dcc05 in thread_start (curthread=0x804e17900) at /usr/src/lib/libthr/thread/thr_create.c:289 #8 0x0000000000000000 in ?? () Backtrace stopped: Cannot access memory at address 0x7fffdff47000 Thread 3 (LWP 101042): #0 0x00000008030ea91c in _umtx_op_err () from /lib/libthr.so.3 #1 0x00000008030debb0 in _thr_umtx_timedwait_uint (mtx=0x80062e020, id=<optimized out>, clockid=<optimized out>, abstime=<optimized out>, shared=<optimized out>) at /usr/src/lib/libthr/thread/thr_umtx.c:234 #2 0x00000008030e8698 in cond_wait_user (abstime=<optimized out>, cancel=1, cvp=<optimized out>, mp=<optimized out>) at /usr/src/lib/libthr/thread/thr_cond.c:304 #3 cond_wait_common (cond=<optimized out>, mutex=<optimized out>, abstime=0x0, cancel=1) at /usr/src/lib/libthr/thread/thr_cond.c:364 #4 0x0000000800ef1608 in apr_queue_pop (queue=0x804e31d08, data=0x7fffdffc0f38) at misc/apr_queue.c:276 #5 0x0000000800ce578b in switch_event_dispatch_thread (thread=<optimized out>, obj=0x804e31d08) at src/switch_event.c:324 #6 0x0000000800f013a6 in dummy_worker (opaque=0x805140780) at threadproc/unix/thread.c:151 #7 0x00000008030dcc05 in thread_start (curthread=0x804e16a00) at /usr/src/lib/libthr/thread/thr_create.c:289 #8 0x0000000000000000 in ?? () Backtrace stopped: Cannot access memory at address 0x7fffdffc1000 Thread 2 (LWP 101041): #0 0x0000000803de665a in _select () from /lib/libc.so.7 #1 0x00000008030df9a2 in __thr_select (numfds=0, readfds=0x0, writefds=0x0, exceptfds=0x0, timeout=0x7fffdfffdf10) at /usr/src/lib/libthr/thread/thr_syscalls.c:493 #2 0x0000000800f02749 in apr_sleep (t=<optimized out>) at time/unix/time.c:246 #3 0x0000000800d4ce25 in do_sleep (t=0) at src/switch_time.c:164 #4 switch_sleep (t=0) at src/switch_time.c:632 #5 0x0000000800c78e51 in pool_thread (thread=<optimized out>, obj=<optimized out>) at src/switch_core_memory.c:569 #6 0x0000000800f013a6 in dummy_worker (opaque=0x804f13740) at threadproc/unix/thread.c:151 #7 0x00000008030dcc05 in thread_start (curthread=0x804e16500) at /usr/src/lib/libthr/thread/thr_create.c:289 #8 0x0000000000000000 in ?? () Backtrace stopped: Cannot access memory at address 0x7fffdfffe000 Thread 1 (LWP 100834): #0 0x00000008030ea91c in _umtx_op_err () from /lib/libthr.so.3 #1 0x00000008030e6f97 in join_common (pthread=0x807ed7700, thread_return=0x7fffffffb658, abstime=0x0) at /usr/src/lib/libthr/thread/thr_join.c:125 #2 0x0000000800f01421 in apr_thread_join (retval=0x7fffffffb68c, thd=0x8059e9658) at threadproc/unix/thread.c:234 #3 0x0000000800cd889e in do_shutdown (module=0x2, shutdown=<optimized out>, unload=SWITCH_TRUE, fail_if_busy=(unknown: 1523667750), err=<optimized out>) at src/switch_loadable_module.c:2031 #4 0x0000000800cdb84a in switch_loadable_module_shutdown () at src/switch_loadable_module.c:2083 #5 0x0000000800c9d92c in switch_core_destroy () at src/switch_core.c:2940 #6 0x0000000000403b2b in main (argc=<optimized out>, argv=0x7fffffffe988) at src/switch.c:1210 ================================================================================ # thread apply all bt full ================================================================================ Thread 10 (LWP 101080): #0 0x0000000803d7797a in _accept () from /lib/libc.so.7 No symbol table info available. #1 0x00000008030df2c6 in __thr_accept (s=67, addr=0x8088f5238, addrlen=0x8088f5218) at /usr/src/lib/libthr/thread/thr_syscalls.c:106 curthread = 0x807ed7700 ret = <optimized out> #2 0x0000000800eff5e0 in apr_socket_accept (new=0x7fffdec1aed8, sock=0x8088f31b0, connection_context=0x8088f5028) at network_io/unix/sockets.c:191 No locals. #3 0x000000080766bbb6 in mod_event_socket_runtime () at mod_event_socket.c:2987 errs = <error reading variable errs (Cannot access memory at address 0x0)> x = <optimized out> pool = <optimized out> listener_pool = 0x8088f5218 sa = 0x8088f30e8 rv = <optimized out> inbound_socket = 0x8088f50e8 listener = <optimized out> #4 0x0000000800cd8da9 in switch_loadable_module_exec (thread=0x8059e9658, obj=0x8059e9238) at src/switch_loadable_module.c:113 ts = 0x8059e9238 status = <error reading variable status (Cannot access memory at address 0x0)> module = 0x8056263c8 #5 0x0000000800f013a6 in dummy_worker (opaque=0x8059e9658) at threadproc/unix/thread.c:151 No locals. #6 0x00000008030dcc05 in thread_start (curthread=0x807ed7700) at /usr/src/lib/libthr/thread/thr_create.c:289 set = {__bits = {0, 0, 0, 0}} #7 0x0000000000000000 in ?? () No symbol table info available. Backtrace stopped: Cannot access memory at address 0x7fffdec1b000 Thread 9 (LWP 101051): #0 0x0000000803d7797a in _accept () from /lib/libc.so.7 No symbol table info available. #1 0x00000008030df2c6 in __thr_accept (s=10, addr=0x806618238, addrlen=0x806618218) at /usr/src/lib/libthr/thread/thr_syscalls.c:106 curthread = 0x804e19200 ret = <optimized out> #2 0x0000000800eff5e0 in apr_socket_accept (new=0x7fffdfa13f20, sock=0x8059de610, connection_context=0x806618028) at network_io/unix/sockets.c:191 No locals. #3 0x0000000800d5ae20 in msrp_listener (thread=<optimized out>, obj=0x8011be5e8 <globals+120>) at src/switch_msrp.c:1379 msock = 0x8011be5e8 <globals+120> sock = 0x8066180e8 thd_attr = <optimized out> pool = 0x806618218 rv = <optimized out> #4 0x0000000800f013a6 in dummy_worker (opaque=0x8059de838) at threadproc/unix/thread.c:151 No locals. #5 0x00000008030dcc05 in thread_start (curthread=0x804e19200) at /usr/src/lib/libthr/thread/thr_create.c:289 set = {__bits = {0, 0, 0, 0}} #6 0x0000000000000000 in ?? () No symbol table info available. Backtrace stopped: Cannot access memory at address 0x7fffdfa14000 Thread 8 (LWP 101050): #0 0x0000000803d7797a in _accept () from /lib/libc.so.7 No symbol table info available. #1 0x00000008030df2c6 in __thr_accept (s=8, addr=0x806018238, addrlen=0x806018218) at /usr/src/lib/libthr/thread/thr_syscalls.c:106 curthread = 0x804e18d00 ret = <optimized out> #2 0x0000000800eff5e0 in apr_socket_accept (new=0x7fffdfa50f20, sock=0x8059de1e8, connection_context=0x806018028) at network_io/unix/sockets.c:191 No locals. #3 0x0000000800d5ae20 in msrp_listener (thread=<optimized out>, obj=0x8011be5c8 <globals+88>) at src/switch_msrp.c:1379 msock = 0x8011be5c8 <globals+88> sock = 0x8060180e8 thd_attr = <optimized out> pool = 0x806018218 rv = <optimized out> #4 0x0000000800f013a6 in dummy_worker (opaque=0x8059de510) at threadproc/unix/thread.c:151 No locals. #5 0x00000008030dcc05 in thread_start (curthread=0x804e18d00) at /usr/src/lib/libthr/thread/thr_create.c:289 set = {__bits = {0, 0, 0, 0}} #6 0x0000000000000000 in ?? () No symbol table info available. Backtrace stopped: Cannot access memory at address 0x7fffdfa51000 Thread 7 (LWP 101047): #0 0x00000008030ea91c in _umtx_op_err () from /lib/libthr.so.3 No symbol table info available. #1 0x00000008030debb0 in _thr_umtx_timedwait_uint (mtx=0x80062e098, id=<optimized out>, clockid=<optimized out>, abstime=<optimized out>, shared=<optimized out>) at /usr/src/lib/libthr/thread/thr_umtx.c:234 tm_p = 0x5a7f38d8 tm_size = <error reading variable tm_size (Cannot access memory at address 0x18)> #2 0x00000008030e8698 in cond_wait_user (abstime=<optimized out>, cancel=1, cvp=<optimized out>, mp=<optimized out>) at /usr/src/lib/libthr/thread/thr_cond.c:304 curthread = 0x804e18300 deferred = <error reading variable deferred (Cannot access memory at address 0x0)> recurse = 0 error = <optimized out> sq = <optimized out> error2 = <optimized out> #3 cond_wait_common (cond=<optimized out>, mutex=<optimized out>, abstime=0x7fffdfe8fec8, cancel=1) at /usr/src/lib/libthr/thread/thr_cond.c:364 cvp = 0x8053de1a0 mp = 0x8051df720 error = <optimized out> #4 0x0000000800efb3ef in apr_thread_cond_timedwait (cond=<optimized out>, mutex=<optimized out>, timeout=500000) at locks/unix/thread_cond.c:89 then = <optimized out> abstime = {tv_sec = 1518287064, tv_nsec = 389979000} rv = <optimized out> #5 0x0000000800ef16fb in apr_queue_pop_timeout (queue=0x8051dd158, data=0x7fffdfe8ff48, timeout=500000) at misc/apr_queue.c:339 rv = 0 #6 0x0000000800cd490b in switch_scheduler_task_thread (thread=<optimized out>, obj=<optimized out>) at src/switch_scheduler.c:188 pop = 0x805e262a0 #7 0x0000000800f013a6 in dummy_worker (opaque=0x805de85c8) at threadproc/unix/thread.c:151 No locals. #8 0x00000008030dcc05 in thread_start (curthread=0x804e18300) at /usr/src/lib/libthr/thread/thr_create.c:289 set = {__bits = {0, 0, 0, 0}} #9 0x0000000000000000 in ?? () No symbol table info available. Backtrace stopped: Cannot access memory at address 0x7fffdfe90000 Thread 6 (LWP 101046): #0 0x0000000803de665a in _select () from /lib/libc.so.7 No symbol table info available. #1 0x00000008030df9a2 in __thr_select (numfds=0, readfds=0x0, writefds=0x0, exceptfds=0x0, timeout=0x7fffdfeccf00) at /usr/src/lib/libthr/thread/thr_syscalls.c:493 curthread = 0x804e18800 ret = <optimized out> #2 0x0000000800f02749 in apr_sleep (t=<optimized out>) at time/unix/time.c:246 tv = {tv_sec = 1, tv_usec = 0} #3 0x0000000800d4ce25 in do_sleep (t=0) at src/switch_time.c:164 ts = {tv_sec = 34375559564, tv_nsec = 34378191880} #4 switch_sleep (t=0) at src/switch_time.c:632 No locals. #5 0x0000000800c86b8d in switch_core_sql_db_thread (thread=<optimized out>, obj=<optimized out>) at src/switch_core_sqldb.c:1395 sec = <error reading variable sec (Cannot access memory at address 0x0)> reg_sec = <error reading variable reg_sec (Cannot access memory at address 0x0)> #6 0x0000000800f013a6 in dummy_worker (opaque=0x805371c48) at threadproc/unix/thread.c:151 No locals. #7 0x00000008030dcc05 in thread_start (curthread=0x804e18800) at /usr/src/lib/libthr/thread/thr_create.c:289 set = {__bits = {0, 0, 0, 0}} #8 0x0000000000000000 in ?? () No symbol table info available. Backtrace stopped: Cannot access memory at address 0x7fffdfecd000 Thread 5 (LWP 101045): #0 0x00000008030ea91c in _umtx_op_err () from /lib/libthr.so.3 No symbol table info available. #1 0x00000008030debb0 in _thr_umtx_timedwait_uint (mtx=0x80062e068, id=<optimized out>, clockid=<optimized out>, abstime=<optimized out>, shared=<optimized out>) at /usr/src/lib/libthr/thread/thr_umtx.c:234 tm_p = 0x8030e17bc <_thr_ast+44> tm_size = <error reading variable tm_size (Cannot access memory at address 0x18)> #2 0x00000008030e8698 in cond_wait_user (abstime=<optimized out>, cancel=1, cvp=<optimized out>, mp=<optimized out>) at /usr/src/lib/libthr/thread/thr_cond.c:304 curthread = 0x804e17e00 deferred = <error reading variable deferred (Cannot access memory at address 0x0)> recurse = 0 error = <optimized out> sq = <optimized out> error2 = <optimized out> #3 cond_wait_common (cond=<optimized out>, mutex=<optimized out>, abstime=0x0, cancel=1) at /usr/src/lib/libthr/thread/thr_cond.c:364 cvp = 0x8053e7a00 mp = 0x8051df4e0 error = <optimized out> #4 0x0000000800c81e46 in switch_user_sql_thread (thread=<optimized out>, obj=0x8051fd0f0) at src/switch_core_sqldb.c:2135 iterations = 0 lc = <optimized out> i = <optimized out> i = <optimized out> #5 0x0000000800f013a6 in dummy_worker (opaque=0x805ad0880) at threadproc/unix/thread.c:151 No locals. #6 0x00000008030dcc05 in thread_start (curthread=0x804e17e00) at /usr/src/lib/libthr/thread/thr_create.c:289 set = {__bits = {0, 0, 0, 0}} #7 0x0000000000000000 in ?? () No symbol table info available. Backtrace stopped: Cannot access memory at address 0x7fffdff0a000 Thread 4 (LWP 101044): #0 0x00000008030ea91c in _umtx_op_err () from /lib/libthr.so.3 No symbol table info available. #1 0x00000008030debb0 in _thr_umtx_timedwait_uint (mtx=0x80062e050, id=<optimized out>, clockid=<optimized out>, abstime=<optimized out>, shared=<optimized out>) at /usr/src/lib/libthr/thread/thr_umtx.c:234 tm_p = 0x8030e17bc <_thr_ast+44> tm_size = <error reading variable tm_size (Cannot access memory at address 0x18)> #2 0x00000008030e8698 in cond_wait_user (abstime=<optimized out>, cancel=1, cvp=<optimized out>, mp=<optimized out>) at /usr/src/lib/libthr/thread/thr_cond.c:304 curthread = 0x804e17900 deferred = <error reading variable deferred (Cannot access memory at address 0x0)> recurse = 0 error = <optimized out> sq = <optimized out> error2 = <optimized out> #3 cond_wait_common (cond=<optimized out>, mutex=<optimized out>, abstime=0x0, cancel=1) at /usr/src/lib/libthr/thread/thr_cond.c:364 cvp = 0x8051d1180 mp = 0x8051d2ea0 error = <optimized out> #4 0x0000000800ef1608 in apr_queue_pop (queue=0x805140908, data=0x7fffdff46f50) at misc/apr_queue.c:276 rv = 0 #5 0x0000000800d40a57 in log_thread (t=<optimized out>, obj=<optimized out>) at src/switch_log.c:294 pop = 0x0 node = <optimized out> binding = <optimized out> #6 0x0000000800f013a6 in dummy_worker (opaque=0x805371b38) at threadproc/unix/thread.c:151 No locals. #7 0x00000008030dcc05 in thread_start (curthread=0x804e17900) at /usr/src/lib/libthr/thread/thr_create.c:289 set = {__bits = {0, 0, 0, 0}} #8 0x0000000000000000 in ?? () No symbol table info available. Backtrace stopped: Cannot access memory at address 0x7fffdff47000 Thread 3 (LWP 101042): #0 0x00000008030ea91c in _umtx_op_err () from /lib/libthr.so.3 No symbol table info available. #1 0x00000008030debb0 in _thr_umtx_timedwait_uint (mtx=0x80062e020, id=<optimized out>, clockid=<optimized out>, abstime=<optimized out>, shared=<optimized out>) at /usr/src/lib/libthr/thread/thr_umtx.c:234 tm_p = 0x8030e17bc <_thr_ast+44> tm_size = <error reading variable tm_size (Cannot access memory at address 0x18)> #2 0x00000008030e8698 in cond_wait_user (abstime=<optimized out>, cancel=1, cvp=<optimized out>, mp=<optimized out>) at /usr/src/lib/libthr/thread/thr_cond.c:304 curthread = 0x804e16a00 deferred = <error reading variable deferred (Cannot access memory at address 0x0)> recurse = 0 error = <optimized out> sq = <optimized out> error2 = <optimized out> #3 cond_wait_common (cond=<optimized out>, mutex=<optimized out>, abstime=0x0, cancel=1) at /usr/src/lib/libthr/thread/thr_cond.c:364 cvp = 0x804ff75c0 mp = 0x804e1da40 error = <optimized out> #4 0x0000000800ef1608 in apr_queue_pop (queue=0x804e31d08, data=0x7fffdffc0f38) at misc/apr_queue.c:276 rv = 0 #5 0x0000000800ce578b in switch_event_dispatch_thread (thread=<optimized out>, obj=0x804e31d08) at src/switch_event.c:324 pop = 0x0 event = <optimized out> #6 0x0000000800f013a6 in dummy_worker (opaque=0x805140780) at threadproc/unix/thread.c:151 No locals. #7 0x00000008030dcc05 in thread_start (curthread=0x804e16a00) at /usr/src/lib/libthr/thread/thr_create.c:289 set = {__bits = {0, 0, 0, 0}} #8 0x0000000000000000 in ?? () No symbol table info available. Backtrace stopped: Cannot access memory at address 0x7fffdffc1000 Thread 2 (LWP 101041): #0 0x0000000803de665a in _select () from /lib/libc.so.7 No symbol table info available. #1 0x00000008030df9a2 in __thr_select (numfds=0, readfds=0x0, writefds=0x0, exceptfds=0x0, timeout=0x7fffdfffdf10) at /usr/src/lib/libthr/thread/thr_syscalls.c:493 curthread = 0x804e16500 ret = <optimized out> #2 0x0000000800f02749 in apr_sleep (t=<optimized out>) at time/unix/time.c:246 tv = {tv_sec = 1, tv_usec = 0} #3 0x0000000800d4ce25 in do_sleep (t=0) at src/switch_time.c:164 ts = {tv_sec = 140736951476056, tv_nsec = 140736951476112} #4 switch_sleep (t=0) at src/switch_time.c:632 No locals. #5 0x0000000800c78e51 in pool_thread (thread=<optimized out>, obj=<optimized out>) at src/switch_core_memory.c:569 len = 0 #6 0x0000000800f013a6 in dummy_worker (opaque=0x804f13740) at threadproc/unix/thread.c:151 No locals. #7 0x00000008030dcc05 in thread_start (curthread=0x804e16500) at /usr/src/lib/libthr/thread/thr_create.c:289 set = {__bits = {0, 0, 0, 0}} #8 0x0000000000000000 in ?? () No symbol table info available. Backtrace stopped: Cannot access memory at address 0x7fffdfffe000 Thread 1 (LWP 100834): #0 0x00000008030ea91c in _umtx_op_err () from /lib/libthr.so.3 No symbol table info available. #1 0x00000008030e6f97 in join_common (pthread=0x807ed7700, thread_return=0x7fffffffb658, abstime=0x0) at /usr/src/lib/libthr/thread/thr_join.c:125 ret = <error reading variable ret (Cannot access memory at address 0x2d)> tid = 101080 ts = {tv_sec = 34411006815, tv_nsec = 34514890792} tsp = 0x18ad8 #2 0x0000000800f01421 in apr_thread_join (retval=0x7fffffffb68c, thd=0x8059e9658) at threadproc/unix/thread.c:234 stat = <optimized out> #3 0x0000000800cd889e in do_shutdown (module=0x2, shutdown=<optimized out>, unload=SWITCH_TRUE, fail_if_busy=(unknown: 1523667750), err=<optimized out>) at src/switch_loadable_module.c:2031 st = SWITCH_STATUS_SUCCESS pool = <optimized out> flags = 12798607 #4 0x0000000800cdb84a in switch_loadable_module_shutdown () at src/switch_loadable_module.c:2083 i = <error reading variable i (Cannot access memory at address 0x0)> hi = <optimized out> val = 0x805626460 module = 0x807ed7700 #5 0x0000000800c9d92c in switch_core_destroy () at src/switch_core.c:2940 event = 0x0 #6 0x0000000000403b2b in main (argc=<optimized out>, argv=0x7fffffffe988) at src/switch.c:1210 pid_path = "/opt/freeswitch/var/run/freeswitch/freeswitch.pid", '\000' <repeats 974 times> pid_buffer = "45848", '\000' <repeats 26 times> old_pid_buffer = "45690", '\000' <repeats 26 times> opts_str = '\000' <repeats 1023 times> local_argv = {0x7fffffffec40 "/opt/freeswitch/bin/freeswitch", 0x7fffffffec5f "-c", 0x0 <repeats 1022 times>} arg_argv = {0x0 <repeats 128 times>} local_argc = 2 reincarnate_reexec = <error reading variable reincarnate_reexec (Cannot access memory at address 0x0)> reincarnate = <error reading variable reincarnate (Cannot access memory at address 0x0)> do_wait = <error reading variable do_wait (Cannot access memory at address 0x0)> nf = <error reading variable nf (Cannot access memory at address 0x0)> pid = <optimized out> nc = <error reading variable nc (Cannot access memory at address 0x1)> ret = <error reading variable ret (Cannot access memory at address 0x0)> flags = 68225 priority = <error reading variable priority (Cannot access memory at address 0x0)> do_kill = 0 run_set = <error reading variable run_set (Cannot access memory at address 0x1)> log_set = 0 alt_base = <error reading variable alt_base (Cannot access memory at address 0x1)> alt_dirs = 0 x = <optimized out> opts = <optimized out> i = <optimized out> runas_group = 0x0 pid_len = 5 pool = <optimized out> fd = <optimized out> old_pid_len = 5 err = 0x800f28b86 "Success" destroy_status = <optimized out> [-- Attachment #3 --] /* Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "apr.h" #include "apr_portable.h" #include "apr_arch_threadproc.h" #if APR_HAS_THREADS #if APR_HAVE_PTHREAD_H /* Destroy the threadattr object */ static apr_status_t threadattr_cleanup(void *data) { apr_threadattr_t *attr = data; apr_status_t rv; rv = pthread_attr_destroy(&attr->attr); #ifdef PTHREAD_SETS_ERRNO if (rv) { rv = errno; } #endif return rv; } APR_DECLARE(apr_status_t) apr_threadattr_create(apr_threadattr_t **new, apr_pool_t *pool) { apr_status_t stat; (*new) = apr_palloc(pool, sizeof(apr_threadattr_t)); (*new)->pool = pool; stat = pthread_attr_init(&(*new)->attr); if (stat == 0) { apr_pool_cleanup_register(pool, *new, threadattr_cleanup, apr_pool_cleanup_null); return APR_SUCCESS; } #ifdef PTHREAD_SETS_ERRNO stat = errno; #endif return stat; } #define DETACH_ARG(v) ((v) ? PTHREAD_CREATE_DETACHED : PTHREAD_CREATE_JOINABLE) APR_DECLARE(apr_status_t) apr_threadattr_detach_set(apr_threadattr_t *attr, apr_int32_t on) { apr_status_t stat; #ifdef PTHREAD_ATTR_SETDETACHSTATE_ARG2_ADDR int arg = DETACH_ARG(v); if ((stat = pthread_attr_setdetachstate(&attr->attr, &arg)) == 0) { #else if ((stat = pthread_attr_setdetachstate(&attr->attr, DETACH_ARG(on))) == 0) { #endif return APR_SUCCESS; } else { #ifdef PTHREAD_SETS_ERRNO stat = errno; #endif return stat; } } APR_DECLARE(apr_status_t) apr_threadattr_detach_get(apr_threadattr_t *attr) { int state; #ifdef PTHREAD_ATTR_GETDETACHSTATE_TAKES_ONE_ARG state = pthread_attr_getdetachstate(&attr->attr); #else pthread_attr_getdetachstate(&attr->attr, &state); #endif if (state == 1) return APR_DETACH; return APR_NOTDETACH; } APR_DECLARE(apr_status_t) apr_threadattr_stacksize_set(apr_threadattr_t *attr, apr_size_t stacksize) { int stat; stat = pthread_attr_setstacksize(&attr->attr, stacksize); if (stat == 0) { return APR_SUCCESS; } #ifdef PTHREAD_SETS_ERRNO stat = errno; #endif return stat; } APR_DECLARE(apr_status_t) apr_threadattr_guardsize_set(apr_threadattr_t *attr, apr_size_t size) { #ifdef HAVE_PTHREAD_ATTR_SETGUARDSIZE apr_status_t rv; rv = pthread_attr_setguardsize(&attr->attr, size); if (rv == 0) { return APR_SUCCESS; } #ifdef PTHREAD_SETS_ERRNO rv = errno; #endif return rv; #else return APR_ENOTIMPL; #endif } static void *dummy_worker(void *opaque) { apr_thread_t *thread = (apr_thread_t*)opaque; #ifdef HAVE_PTHREAD_SETSCHEDPARAM if (thread->priority) { int policy; struct sched_param param = { 0 }; pthread_t tt = pthread_self(); pthread_getschedparam(tt, &policy, ¶m); param.sched_priority = thread->priority; pthread_setschedparam(tt, policy, ¶m); } #endif return thread->func(thread, thread->data); } APR_DECLARE(apr_status_t) apr_thread_create(apr_thread_t **new, apr_threadattr_t *attr, apr_thread_start_t func, void *data, apr_pool_t *pool) { apr_status_t stat; pthread_attr_t *temp; pthread_t tt; (*new) = (apr_thread_t *)apr_pcalloc(pool, sizeof(apr_thread_t)); if ((*new) == NULL) { return APR_ENOMEM; } (*new)->td = (pthread_t *)apr_pcalloc(pool, sizeof(pthread_t)); if ((*new)->td == NULL) { return APR_ENOMEM; } (*new)->pool = pool; (*new)->data = data; (*new)->func = func; if (attr) temp = &attr->attr; else temp = NULL; stat = apr_pool_create(&(*new)->pool, pool); if (stat != APR_SUCCESS) { return stat; } if (attr && attr->priority) { (*new)->priority = attr->priority; } if ((stat = pthread_create(&tt, temp, dummy_worker, (*new))) == 0) { *(*new)->td = tt; return APR_SUCCESS; } else { #ifdef PTHREAD_SETS_ERRNO stat = errno; #endif return stat; } } APR_DECLARE(apr_os_thread_t) apr_os_thread_current(void) { return pthread_self(); } APR_DECLARE(int) apr_os_thread_equal(apr_os_thread_t tid1, apr_os_thread_t tid2) { return pthread_equal(tid1, tid2); } APR_DECLARE(apr_status_t) apr_thread_exit(apr_thread_t *thd, apr_status_t retval) { thd->exitval = retval; apr_pool_destroy(thd->pool); pthread_exit(NULL); return APR_SUCCESS; } APR_DECLARE(apr_status_t) apr_thread_join(apr_status_t *retval, apr_thread_t *thd) { apr_status_t stat; apr_status_t *thread_stat; if ((stat = pthread_join(*thd->td,(void *)&thread_stat)) == 0) { *retval = thd->exitval; return APR_SUCCESS; } else { #ifdef PTHREAD_SETS_ERRNO stat = errno; #endif return stat; } } APR_DECLARE(apr_status_t) apr_thread_detach(apr_thread_t *thd) { apr_status_t stat; #ifdef PTHREAD_DETACH_ARG1_ADDR if ((stat = pthread_detach(thd->td)) == 0) { #else if ((stat = pthread_detach(*thd->td)) == 0) { #endif return APR_SUCCESS; } else { #ifdef PTHREAD_SETS_ERRNO stat = errno; #endif return stat; } } void apr_thread_yield() { } APR_DECLARE(apr_status_t) apr_thread_data_get(void **data, const char *key, apr_thread_t *thread) { return apr_pool_userdata_get(data, key, thread->pool); } APR_DECLARE(apr_status_t) apr_thread_data_set(void *data, const char *key, apr_status_t (*cleanup)(void *), apr_thread_t *thread) { return apr_pool_userdata_set(data, key, cleanup, thread->pool); } APR_DECLARE(apr_status_t) apr_os_thread_get(apr_os_thread_t **thethd, apr_thread_t *thd) { *thethd = thd->td; return APR_SUCCESS; } APR_DECLARE(apr_status_t) apr_os_thread_put(apr_thread_t **thd, apr_os_thread_t *thethd, apr_pool_t *pool) { if (pool == NULL) { return APR_ENOPOOL; } if ((*thd) == NULL) { (*thd) = (apr_thread_t *)apr_pcalloc(pool, sizeof(apr_thread_t)); (*thd)->pool = pool; } (*thd)->td = thethd; return APR_SUCCESS; } APR_DECLARE(apr_status_t) apr_thread_once_init(apr_thread_once_t **control, apr_pool_t *p) { static const pthread_once_t once_init = PTHREAD_ONCE_INIT; *control = apr_palloc(p, sizeof(**control)); (*control)->once = once_init; return APR_SUCCESS; } APR_DECLARE(apr_status_t) apr_thread_once(apr_thread_once_t *control, void (*func)(void)) { return pthread_once(&control->once, func); } APR_POOL_IMPLEMENT_ACCESSOR(thread) #endif /* HAVE_PTHREAD_H */ #endif /* APR_HAS_THREADS */ #if !APR_HAS_THREADS /* avoid warning for no prototype */ APR_DECLARE(apr_status_t) apr_os_thread_get(void); APR_DECLARE(apr_status_t) apr_os_thread_get(void) { return APR_ENOTIMPL; } #endif [-- Attachment #4 --] /* * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application * Copyright (C) 2005-2014, Anthony Minessale II <anthm@freeswitch.org> * * Version: MPL 1.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application * * The Initial Developer of the Original Code is * Anthony Minessale II <anthm@freeswitch.org> * Portions created by the Initial Developer are Copyright (C) * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Anthony Minessale II <anthm@freeswitch.org> * Seven Du <dujinfang@gmail.com> * * switch_loadable_module.c -- Loadable Modules * */ #include <switch.h> /* for apr_pstrcat */ #include <apr_strings.h> /* for apr_env_get and apr_env_set */ #include <apr_env.h> /* for apr file and directory handling */ #include <apr_file_io.h> typedef struct switch_file_node_s { const switch_file_interface_t *ptr; const char *interface_name; struct switch_file_node_s *next; } switch_file_node_t; typedef struct switch_codec_node_s { const switch_codec_interface_t *ptr; const char *interface_name; struct switch_codec_node_s *next; } switch_codec_node_t; struct switch_loadable_module { char *key; char *filename; int perm; switch_loadable_module_interface_t *module_interface; switch_dso_lib_t lib; switch_module_load_t switch_module_load; switch_module_runtime_t switch_module_runtime; switch_module_shutdown_t switch_module_shutdown; switch_memory_pool_t *pool; switch_status_t status; switch_thread_t *thread; switch_bool_t shutting_down; }; struct switch_loadable_module_container { switch_hash_t *module_hash; switch_hash_t *endpoint_hash; switch_hash_t *codec_hash; switch_hash_t *dialplan_hash; switch_hash_t *timer_hash; switch_hash_t *application_hash; switch_hash_t *chat_application_hash; switch_hash_t *api_hash; switch_hash_t *json_api_hash; switch_hash_t *file_hash; switch_hash_t *speech_hash; switch_hash_t *asr_hash; switch_hash_t *directory_hash; switch_hash_t *chat_hash; switch_hash_t *say_hash; switch_hash_t *management_hash; switch_hash_t *limit_hash; switch_hash_t *secondary_recover_hash; switch_mutex_t *mutex; switch_memory_pool_t *pool; }; static struct switch_loadable_module_container loadable_modules; static switch_status_t do_shutdown(switch_loadable_module_t *module, switch_bool_t shutdown, switch_bool_t unload, switch_bool_t fail_if_busy, const char **err); static switch_status_t switch_loadable_module_load_module_ex(char *dir, char *fname, switch_bool_t runtime, switch_bool_t global, const char **err); static void *SWITCH_THREAD_FUNC switch_loadable_module_exec(switch_thread_t *thread, void *obj) { switch_status_t status = SWITCH_STATUS_SUCCESS; switch_core_thread_session_t *ts = obj; switch_loadable_module_t *module = ts->objs[0]; int restarts; switch_assert(thread != NULL); switch_assert(module != NULL); for (restarts = 0; status != SWITCH_STATUS_TERM && !module->shutting_down; restarts++) { status = module->switch_module_runtime(); } switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Thread ended for %s\n", module->module_interface->module_name); if (ts->pool) { switch_memory_pool_t *pool = ts->pool; switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Destroying Pool for %s\n", module->module_interface->module_name); switch_core_destroy_memory_pool(&pool); } switch_thread_exit(thread, 0); return NULL; } static void switch_loadable_module_runtime(void) { switch_hash_index_t *hi; void *val; switch_loadable_module_t *module; switch_mutex_lock(loadable_modules.mutex); for (hi = switch_core_hash_first(loadable_modules.module_hash); hi; hi = switch_core_hash_next(&hi)) { switch_core_hash_this(hi, NULL, NULL, &val); module = (switch_loadable_module_t *) val; if (module->switch_module_runtime) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Starting runtime thread for %s\n", module->module_interface->module_name); module->thread = switch_core_launch_thread(switch_loadable_module_exec, module, loadable_modules.pool); } } switch_mutex_unlock(loadable_modules.mutex); } static switch_status_t switch_loadable_module_process(char *key, switch_loadable_module_t *new_module) { switch_event_t *event; int added = 0; new_module->key = switch_core_strdup(new_module->pool, key); switch_mutex_lock(loadable_modules.mutex); switch_core_hash_insert(loadable_modules.module_hash, key, new_module); if (new_module->module_interface->endpoint_interface) { const switch_endpoint_interface_t *ptr; for (ptr = new_module->module_interface->endpoint_interface; ptr; ptr = ptr->next) { if (!ptr->interface_name) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to load endpoint interface from %s due to no interface name.\n", key); } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Adding Endpoint '%s'\n", ptr->interface_name); switch_core_hash_insert(loadable_modules.endpoint_hash, ptr->interface_name, (const void *) ptr); if (switch_event_create(&event, SWITCH_EVENT_MODULE_LOAD) == SWITCH_STATUS_SUCCESS) { switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "type", "endpoint"); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "name", ptr->interface_name); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "key", new_module->key); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "filename", new_module->filename); switch_event_fire(&event); added++; } } } } if (new_module->module_interface->codec_interface) { const switch_codec_implementation_t *impl; const switch_codec_interface_t *ptr; for (ptr = new_module->module_interface->codec_interface; ptr; ptr = ptr->next) { if (!ptr->interface_name) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to load codec interface from %s due to no interface name.\n", key); } else { unsigned load_interface = 1; switch_codec_node_t *node, *head; for (impl = ptr->implementations; impl; impl = impl->next) { if (!impl->iananame) { load_interface = 0; switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to load codec interface %s from %s due to no iana name in an implementation.\n", ptr->interface_name, key); break; } if (impl->decoded_bytes_per_packet > SWITCH_RECOMMENDED_BUFFER_SIZE) { load_interface = 0; switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to load codec interface %s from %s due to bytes per frame %d exceeding buffer size %d.\n", ptr->interface_name, key, impl->decoded_bytes_per_packet, SWITCH_RECOMMENDED_BUFFER_SIZE); break; } } if (load_interface) { for (impl = ptr->implementations; impl; impl = impl->next) { if (impl->bits_per_second) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Adding Codec %s %d %s %dhz %dms %dch %dbps\n", impl->iananame, impl->ianacode, ptr->interface_name, impl->actual_samples_per_second, impl->microseconds_per_packet / 1000, impl->number_of_channels, impl->bits_per_second); } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Adding Codec %s %d %s %dhz %dms %dch (VBR)\n", impl->iananame, impl->ianacode, ptr->interface_name, impl->actual_samples_per_second, impl->microseconds_per_packet / 1000, impl->number_of_channels); } node = switch_core_alloc(new_module->pool, sizeof(*node)); node->ptr = ptr; node->interface_name = switch_core_strdup(new_module->pool, new_module->module_interface->module_name); if ((head = switch_core_hash_find(loadable_modules.codec_hash, impl->iananame))) { node->next = head; } switch_core_hash_insert(loadable_modules.codec_hash, impl->iananame, (const void *) node); } if (switch_event_create(&event, SWITCH_EVENT_MODULE_LOAD) == SWITCH_STATUS_SUCCESS) { switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "type", "codec"); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "name", ptr->interface_name); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "key", new_module->key); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "filename", new_module->filename); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "module", new_module->module_interface->module_name); switch_event_fire(&event); added++; } } } } } if (new_module->module_interface->dialplan_interface) { const switch_dialplan_interface_t *ptr; for (ptr = new_module->module_interface->dialplan_interface; ptr; ptr = ptr->next) { if (!ptr->interface_name) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to load dialplan interface from %s due to no interface name.\n", key); } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Adding Dialplan '%s'\n", ptr->interface_name); if (switch_event_create(&event, SWITCH_EVENT_MODULE_LOAD) == SWITCH_STATUS_SUCCESS) { switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "type", "dialplan"); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "name", ptr->interface_name); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "key", new_module->key); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "filename", new_module->filename); switch_event_fire(&event); added++; } switch_core_hash_insert(loadable_modules.dialplan_hash, ptr->interface_name, (const void *) ptr); } } } if (new_module->module_interface->timer_interface) { const switch_timer_interface_t *ptr; for (ptr = new_module->module_interface->timer_interface; ptr; ptr = ptr->next) { if (!ptr->interface_name) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to load timer interface from %s due to no interface name.\n", key); } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Adding Timer '%s'\n", ptr->interface_name); if (switch_event_create(&event, SWITCH_EVENT_MODULE_LOAD) == SWITCH_STATUS_SUCCESS) { switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "type", "timer"); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "name", ptr->interface_name); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "key", new_module->key); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "filename", new_module->filename); switch_event_fire(&event); added++; } switch_core_hash_insert(loadable_modules.timer_hash, ptr->interface_name, (const void *) ptr); } } } if (new_module->module_interface->application_interface) { const switch_application_interface_t *ptr; for (ptr = new_module->module_interface->application_interface; ptr; ptr = ptr->next) { if (!ptr->interface_name) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to load application interface from %s due to no interface name.\n", key); } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Adding Application '%s'\n", ptr->interface_name); if (switch_event_create(&event, SWITCH_EVENT_MODULE_LOAD) == SWITCH_STATUS_SUCCESS) { switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "type", "application"); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "name", ptr->interface_name); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "description", switch_str_nil(ptr->short_desc)); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "syntax", switch_str_nil(ptr->syntax)); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "key", new_module->key); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "filename", new_module->filename); switch_event_fire(&event); added++; } switch_core_hash_insert(loadable_modules.application_hash, ptr->interface_name, (const void *) ptr); } } } if (new_module->module_interface->chat_application_interface) { const switch_chat_application_interface_t *ptr; for (ptr = new_module->module_interface->chat_application_interface; ptr; ptr = ptr->next) { if (!ptr->interface_name) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to load application interface from %s due to no interface name.\n", key); } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Adding Chat Application '%s'\n", ptr->interface_name); if (switch_event_create(&event, SWITCH_EVENT_MODULE_LOAD) == SWITCH_STATUS_SUCCESS) { switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "type", "application"); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "name", ptr->interface_name); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "description", switch_str_nil(ptr->short_desc)); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "syntax", switch_str_nil(ptr->syntax)); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "key", new_module->key); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "filename", new_module->filename); switch_event_fire(&event); added++; } switch_core_hash_insert(loadable_modules.chat_application_hash, ptr->interface_name, (const void *) ptr); } } } if (new_module->module_interface->api_interface) { const switch_api_interface_t *ptr; for (ptr = new_module->module_interface->api_interface; ptr; ptr = ptr->next) { if (!ptr->interface_name) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to load api interface from %s due to no interface name.\n", key); } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Adding API Function '%s'\n", ptr->interface_name); if (switch_event_create(&event, SWITCH_EVENT_MODULE_LOAD) == SWITCH_STATUS_SUCCESS) { switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "type", "api"); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "name", ptr->interface_name); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "description", switch_str_nil(ptr->desc)); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "syntax", switch_str_nil(ptr->syntax)); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "key", new_module->key); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "filename", new_module->filename); switch_event_fire(&event); added++; } switch_core_hash_insert(loadable_modules.api_hash, ptr->interface_name, (const void *) ptr); } } } if (new_module->module_interface->json_api_interface) { const switch_json_api_interface_t *ptr; for (ptr = new_module->module_interface->json_api_interface; ptr; ptr = ptr->next) { if (!ptr->interface_name) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to load JSON api interface from %s due to no interface name.\n", key); } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Adding JSON API Function '%s'\n", ptr->interface_name); if (switch_event_create(&event, SWITCH_EVENT_MODULE_LOAD) == SWITCH_STATUS_SUCCESS) { switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "type", "json_api"); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "name", ptr->interface_name); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "description", switch_str_nil(ptr->desc)); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "syntax", switch_str_nil(ptr->syntax)); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "key", new_module->key); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "filename", new_module->filename); switch_event_fire(&event); added++; } switch_core_hash_insert(loadable_modules.json_api_hash, ptr->interface_name, (const void *) ptr); } } } if (new_module->module_interface->file_interface) { const switch_file_interface_t *ptr; for (ptr = new_module->module_interface->file_interface; ptr; ptr = ptr->next) { if (!ptr->interface_name) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to load file interface from %s due to no interface name.\n", key); } else if (!ptr->extens) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to load file interface from %s due to no file extensions.\n", key); } else { int i; switch_file_node_t *node, *head; for (i = 0; ptr->extens[i]; i++) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Adding File Format '%s'\n", ptr->extens[i]); if (switch_event_create(&event, SWITCH_EVENT_MODULE_LOAD) == SWITCH_STATUS_SUCCESS) { switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "type", "file"); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "name", ptr->extens[i]); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "key", new_module->key); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "filename", new_module->filename); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "module", new_module->module_interface->module_name); switch_event_fire(&event); added++; } node = switch_core_alloc(new_module->pool, sizeof(*node)); node->ptr = ptr; node->interface_name = switch_core_strdup(new_module->pool, new_module->module_interface->module_name); if ((head = switch_core_hash_find(loadable_modules.file_hash, ptr->extens[i]))) { node->next = head; } switch_core_hash_insert(loadable_modules.file_hash, ptr->extens[i], (const void *) node); } } } } if (new_module->module_interface->speech_interface) { const switch_speech_interface_t *ptr; for (ptr = new_module->module_interface->speech_interface; ptr; ptr = ptr->next) { if (!ptr->interface_name) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to load speech interface from %s due to no interface name.\n", key); } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Adding Speech interface '%s'\n", ptr->interface_name); if (switch_event_create(&event, SWITCH_EVENT_MODULE_LOAD) == SWITCH_STATUS_SUCCESS) { switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "type", "speech"); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "name", ptr->interface_name); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "key", new_module->key); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "filename", new_module->filename); switch_event_fire(&event); added++; } switch_core_hash_insert(loadable_modules.speech_hash, ptr->interface_name, (const void *) ptr); } } } if (new_module->module_interface->asr_interface) { const switch_asr_interface_t *ptr; for (ptr = new_module->module_interface->asr_interface; ptr; ptr = ptr->next) { if (!ptr->interface_name) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to load asr interface from %s due to no interface name.\n", key); } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Adding ASR interface '%s'\n", ptr->interface_name); if (switch_event_create(&event, SWITCH_EVENT_MODULE_LOAD) == SWITCH_STATUS_SUCCESS) { switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "type", "asr"); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "name", ptr->interface_name); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "key", new_module->key); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "filename", new_module->filename); switch_event_fire(&event); added++; } switch_core_hash_insert(loadable_modules.asr_hash, ptr->interface_name, (const void *) ptr); } } } if (new_module->module_interface->directory_interface) { const switch_directory_interface_t *ptr; for (ptr = new_module->module_interface->directory_interface; ptr; ptr = ptr->next) { if (!ptr->interface_name) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to load directory interface from %s due to no interface name.\n", key); } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Adding Directory interface '%s'\n", ptr->interface_name); if (switch_event_create(&event, SWITCH_EVENT_MODULE_LOAD) == SWITCH_STATUS_SUCCESS) { switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "type", "directory"); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "name", ptr->interface_name); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "key", new_module->key); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "filename", new_module->filename); switch_event_fire(&event); added++; } switch_core_hash_insert(loadable_modules.directory_hash, ptr->interface_name, (const void *) ptr); } } } if (new_module->module_interface->chat_interface) { const switch_chat_interface_t *ptr; for (ptr = new_module->module_interface->chat_interface; ptr; ptr = ptr->next) { if (!ptr->interface_name) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to load chat interface from %s due to no interface name.\n", key); } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Adding Chat interface '%s'\n", ptr->interface_name); if (switch_event_create(&event, SWITCH_EVENT_MODULE_LOAD) == SWITCH_STATUS_SUCCESS) { switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "type", "chat"); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "name", ptr->interface_name); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "key", new_module->key); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "filename", new_module->filename); switch_event_fire(&event); added++; } switch_core_hash_insert(loadable_modules.chat_hash, ptr->interface_name, (const void *) ptr); } } } if (new_module->module_interface->say_interface) { const switch_say_interface_t *ptr; for (ptr = new_module->module_interface->say_interface; ptr; ptr = ptr->next) { if (!ptr->interface_name) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to load say interface from %s due to no interface name.\n", key); } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Adding Say interface '%s'\n", ptr->interface_name); if (switch_event_create(&event, SWITCH_EVENT_MODULE_LOAD) == SWITCH_STATUS_SUCCESS) { switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "type", "say"); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "name", ptr->interface_name); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "key", new_module->key); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "filename", new_module->filename); switch_event_fire(&event); added++; } switch_core_hash_insert(loadable_modules.say_hash, ptr->interface_name, (const void *) ptr); } } } if (new_module->module_interface->management_interface) { const switch_management_interface_t *ptr; for (ptr = new_module->module_interface->management_interface; ptr; ptr = ptr->next) { if (!ptr->relative_oid) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to load management interface from %s due to no interface name.\n", key); } else { if (switch_core_hash_find(loadable_modules.management_hash, ptr->relative_oid)) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to load management interface %s. OID %s already exists\n", key, ptr->relative_oid); } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Adding Management interface '%s' OID[%s.%s]\n", key, FREESWITCH_OID_PREFIX, ptr->relative_oid); switch_core_hash_insert(loadable_modules.management_hash, ptr->relative_oid, (const void *) ptr); if (switch_event_create(&event, SWITCH_EVENT_MODULE_LOAD) == SWITCH_STATUS_SUCCESS) { switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "type", "management"); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "name", ptr->relative_oid); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "key", new_module->key); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "filename", new_module->filename); switch_event_fire(&event); added++; } } } } } if (new_module->module_interface->limit_interface) { const switch_limit_interface_t *ptr; for (ptr = new_module->module_interface->limit_interface; ptr; ptr = ptr->next) { if (!ptr->interface_name) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to load limit interface from %s due to no interface name.\n", key); } else { if (switch_core_hash_find(loadable_modules.limit_hash, ptr->interface_name)) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to load limit interface %s. Name %s already exists\n", key, ptr->interface_name); } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Adding Limit interface '%s'\n", ptr->interface_name); switch_core_hash_insert(loadable_modules.limit_hash, ptr->interface_name, (const void *) ptr); if (switch_event_create(&event, SWITCH_EVENT_MODULE_LOAD) == SWITCH_STATUS_SUCCESS) { switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "type", "limit"); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "name", ptr->interface_name); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "key", new_module->key); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "filename", new_module->filename); switch_event_fire(&event); added++; } } } } } if (!added) { if (switch_event_create(&event, SWITCH_EVENT_MODULE_LOAD) == SWITCH_STATUS_SUCCESS) { switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "type", "generic"); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "name", new_module->key); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "key", new_module->key); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "filename", new_module->filename); switch_event_fire(&event); added++; } } switch_mutex_unlock(loadable_modules.mutex); return SWITCH_STATUS_SUCCESS; } #define CHAT_MAX_MSG_QUEUE 101 #define CHAT_QUEUE_SIZE 5000 static struct { switch_queue_t *msg_queue[CHAT_MAX_MSG_QUEUE]; switch_thread_t *msg_queue_thread[CHAT_MAX_MSG_QUEUE]; int msg_queue_len; switch_mutex_t *mutex; switch_memory_pool_t *pool; int running; } chat_globals; static int IDX = 0; static switch_status_t do_chat_send(switch_event_t *message_event) { switch_chat_interface_t *ci; switch_status_t status = SWITCH_STATUS_FALSE; switch_hash_index_t *hi; switch_event_t *dup = NULL; const void *var; void *val; const char *proto; const char *replying; const char *dest_proto; int do_skip = 0; /* const char *from; const char *to; const char *subject; const char *body; const char *type; const char *hint; */ dest_proto = switch_event_get_header(message_event, "dest_proto"); if (!dest_proto) { return SWITCH_STATUS_FALSE; } /* from = switch_event_get_header(message_event, "from"); to = switch_event_get_header(message_event, "to"); subject = switch_event_get_header(message_event, "subject"); body = switch_event_get_body(message_event); type = switch_event_get_header(message_event, "type"); hint = switch_event_get_header(message_event, "hint"); */ if (!(proto = switch_event_get_header(message_event, "proto"))) { proto = "global"; switch_event_add_header_string(message_event, SWITCH_STACK_BOTTOM, "proto", proto); } replying = switch_event_get_header(message_event, "replying"); if (!switch_true(replying) && !switch_stristr("global", proto) && !switch_true(switch_event_get_header(message_event, "skip_global_process"))) { switch_mutex_lock(loadable_modules.mutex); for (hi = switch_core_hash_first(loadable_modules.chat_hash); hi; hi = switch_core_hash_next(&hi)) { switch_core_hash_this(hi, &var, NULL, &val); if ((ci = (switch_chat_interface_t *) val)) { if (ci->chat_send && !strncasecmp(ci->interface_name, "GLOBAL_", 7)) { status = ci->chat_send(message_event); if (status == SWITCH_STATUS_SUCCESS) { if (switch_true(switch_event_get_header(message_event, "final_delivery"))) { /* The event was handled by an extension in the chatplan, * so the event will be duplicated, modified and queued again, * but it won't be processed by the chatplan again. * So this copy of the event can be destroyed by the caller. */ do_skip = 1; } } else if (status == SWITCH_STATUS_BREAK) { /* The event went through the chatplan, but no extension matched * to handle the sms messsage. It'll be attempted to be delivered * directly, and unless that works the sms delivery will have failed. */ } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Chat Interface Error [%s]!\n", dest_proto); break; } } } } switch_safe_free(hi); switch_mutex_unlock(loadable_modules.mutex); } if (!do_skip && !switch_stristr("GLOBAL", dest_proto)) { if ((ci = switch_loadable_module_get_chat_interface(dest_proto)) && ci->chat_send) { status = ci->chat_send(message_event); UNPROTECT_INTERFACE(ci); } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid chat interface [%s]!\n", dest_proto); status = SWITCH_STATUS_FALSE; } } switch_event_dup(&dup, message_event); if ( switch_true(switch_event_get_header(message_event, "blocking")) ) { if (status == SWITCH_STATUS_SUCCESS) { switch_event_add_header_string(dup, SWITCH_STACK_BOTTOM, "Delivery-Failure", "false"); } else { switch_event_add_header_string(dup, SWITCH_STACK_BOTTOM, "Delivery-Failure", "true"); } } else { switch_event_add_header_string(dup, SWITCH_STACK_BOTTOM, "Nonblocking-Delivery", "true"); } switch_event_fire(&dup); return status; } static switch_status_t chat_process_event(switch_event_t **eventp) { switch_event_t *event; switch_status_t status; switch_assert(eventp); event = *eventp; *eventp = NULL; status = do_chat_send(event); switch_event_destroy(&event); return status; } void *SWITCH_THREAD_FUNC chat_thread_run(switch_thread_t *thread, void *obj) { void *pop; switch_queue_t *q = (switch_queue_t *) obj; switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Chat Thread Started\n"); while(switch_queue_pop(q, &pop) == SWITCH_STATUS_SUCCESS && pop) { switch_event_t *event = (switch_event_t *) pop; chat_process_event(&event); switch_cond_next(); } switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Chat Thread Ended\n"); return NULL; } static void chat_thread_start(int idx) { if (idx >= CHAT_MAX_MSG_QUEUE || (idx < chat_globals.msg_queue_len && chat_globals.msg_queue_thread[idx])) { return; } switch_mutex_lock(chat_globals.mutex); if (idx >= chat_globals.msg_queue_len) { int i; chat_globals.msg_queue_len = idx + 1; for (i = 0; i < chat_globals.msg_queue_len; i++) { if (!chat_globals.msg_queue[i]) { switch_threadattr_t *thd_attr = NULL; switch_queue_create(&chat_globals.msg_queue[i], CHAT_QUEUE_SIZE, chat_globals.pool); switch_threadattr_create(&thd_attr, chat_globals.pool); switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE); switch_thread_create(&chat_globals.msg_queue_thread[i], thd_attr, chat_thread_run, chat_globals.msg_queue[i], chat_globals.pool); } } } switch_mutex_unlock(chat_globals.mutex); } static void chat_queue_message(switch_event_t **eventp) { int idx = 0; switch_event_t *event; switch_assert(eventp); event = *eventp; *eventp = NULL; if (chat_globals.running == 0) { chat_process_event(&event); return; } again: switch_mutex_lock(chat_globals.mutex); idx = IDX; IDX++; if (IDX >= chat_globals.msg_queue_len) IDX = 0; switch_mutex_unlock(chat_globals.mutex); chat_thread_start(idx); if (switch_queue_trypush(chat_globals.msg_queue[idx], event) != SWITCH_STATUS_SUCCESS) { if (chat_globals.msg_queue_len < CHAT_MAX_MSG_QUEUE) { chat_thread_start(idx + 1); goto again; } else { switch_queue_push(chat_globals.msg_queue[idx], event); } } } SWITCH_DECLARE(switch_status_t) switch_core_execute_chat_app(switch_event_t *message, const char *app, const char *data) { switch_chat_application_interface_t *cai; switch_status_t status = SWITCH_STATUS_SUCCESS; char *expanded; if (!(cai = switch_loadable_module_get_chat_application_interface(app)) || !cai->chat_application_function) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid chat application interface [%s]!\n", app); return SWITCH_STATUS_FALSE; } if (switch_test_flag(message, EF_NO_CHAT_EXEC)) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Message is not allowed to execute apps\n"); switch_goto_status(SWITCH_STATUS_FALSE, end); } if (data && !strcmp(data, "__undef")) { data = NULL; } expanded = switch_event_expand_headers(message, data); status = cai->chat_application_function(message, expanded); if (expanded != data) { free(expanded); } end: UNPROTECT_INTERFACE(cai); return status; } SWITCH_DECLARE(switch_status_t) switch_core_chat_send_args(const char *dest_proto, const char *proto, const char *from, const char *to, const char *subject, const char *body, const char *type, const char *hint, switch_bool_t blocking) { switch_event_t *message_event; switch_status_t status; if (switch_event_create(&message_event, SWITCH_EVENT_MESSAGE) == SWITCH_STATUS_SUCCESS) { switch_event_add_header_string(message_event, SWITCH_STACK_BOTTOM, "proto", proto); switch_event_add_header_string(message_event, SWITCH_STACK_BOTTOM, "from", from); switch_event_add_header_string(message_event, SWITCH_STACK_BOTTOM, "to", to); switch_event_add_header_string(message_event, SWITCH_STACK_BOTTOM, "subject", subject); switch_event_add_header_string(message_event, SWITCH_STACK_BOTTOM, "type", type); switch_event_add_header_string(message_event, SWITCH_STACK_BOTTOM, "hint", hint); switch_event_add_header_string(message_event, SWITCH_STACK_BOTTOM, "skip_global_process", "true"); if (blocking) { switch_event_add_header_string(message_event, SWITCH_STACK_BOTTOM, "blocking", "true"); } if (body) { switch_event_add_body(message_event, "%s", body); } } else { abort(); } if (dest_proto) { switch_event_add_header_string(message_event, SWITCH_STACK_BOTTOM, "dest_proto", dest_proto); } if (blocking) { status = chat_process_event(&message_event); } else { chat_queue_message(&message_event); status = SWITCH_STATUS_SUCCESS; } return status; } SWITCH_DECLARE(switch_status_t) switch_core_chat_send(const char *dest_proto, switch_event_t *message_event) { switch_event_t *dup; switch_event_dup(&dup, message_event); if (dest_proto) { switch_event_add_header_string(dup, SWITCH_STACK_BOTTOM, "dest_proto", dest_proto); } chat_queue_message(&dup); return SWITCH_STATUS_SUCCESS; } SWITCH_DECLARE(switch_status_t) switch_core_chat_deliver(const char *dest_proto, switch_event_t **message_event) { if (dest_proto) { switch_event_add_header_string(*message_event, SWITCH_STACK_BOTTOM, "dest_proto", dest_proto); } chat_queue_message(message_event); return SWITCH_STATUS_SUCCESS; } static switch_status_t switch_loadable_module_unprocess(switch_loadable_module_t *old_module) { switch_event_t *event; int removed = 0; switch_mutex_lock(loadable_modules.mutex); if (old_module->module_interface->endpoint_interface) { const switch_endpoint_interface_t *ptr; for (ptr = old_module->module_interface->endpoint_interface; ptr; ptr = ptr->next) { if (ptr->interface_name) { switch_core_session_hupall_endpoint(ptr, SWITCH_CAUSE_MANAGER_REQUEST); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Write lock interface '%s' to wait for existing references.\n", ptr->interface_name); if (switch_thread_rwlock_trywrlock_timeout(ptr->rwlock, 10) == SWITCH_STATUS_SUCCESS) { switch_thread_rwlock_unlock(ptr->rwlock); } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Giving up on '%s' waiting for existing references.\n", ptr->interface_name); } switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Deleting Endpoint '%s'\n", ptr->interface_name); if (switch_event_create(&event, SWITCH_EVENT_MODULE_UNLOAD) == SWITCH_STATUS_SUCCESS) { switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "type", "endpoint"); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "name", ptr->interface_name); switch_event_fire(&event); removed++; } switch_core_hash_delete(loadable_modules.endpoint_hash, ptr->interface_name); } } } if (old_module->module_interface->codec_interface) { const switch_codec_implementation_t *impl; const switch_codec_interface_t *ptr; switch_codec_node_t *node, *head, *last = NULL; for (ptr = old_module->module_interface->codec_interface; ptr; ptr = ptr->next) { if (ptr->interface_name) { unsigned load_interface = 1; for (impl = ptr->implementations; impl; impl = impl->next) { if (!impl->iananame) { load_interface = 0; break; } } if (load_interface) { for (impl = ptr->implementations; impl; impl = impl->next) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Deleting Codec %s %d %s %dhz %dms\n", impl->iananame, impl->ianacode, ptr->interface_name, impl->actual_samples_per_second, impl->microseconds_per_packet / 1000); switch_core_session_hupall_matching_var("read_codec", impl->iananame, SWITCH_CAUSE_MANAGER_REQUEST); switch_core_session_hupall_matching_var("write_codec", impl->iananame, SWITCH_CAUSE_MANAGER_REQUEST); if ((head = switch_core_hash_find(loadable_modules.codec_hash, impl->iananame))) { for(node = head; node; node = node->next) { if (!strcmp(node->interface_name, old_module->module_interface->module_name)) { if (node == head) { if ((node = node->next)) { switch_core_hash_insert(loadable_modules.codec_hash, impl->iananame, (const void *) node); } else { switch_core_hash_delete(loadable_modules.codec_hash, impl->iananame); } } else { if (last) { last->next = node->next; } } break; } last = node; } } } if (switch_event_create(&event, SWITCH_EVENT_MODULE_UNLOAD) == SWITCH_STATUS_SUCCESS) { switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "type", "codec"); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "name", ptr->interface_name); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "module", old_module->module_interface->module_name); switch_event_fire(&event); removed++; } } } } } if (old_module->module_interface->dialplan_interface) { const switch_dialplan_interface_t *ptr; for (ptr = old_module->module_interface->dialplan_interface; ptr; ptr = ptr->next) { if (ptr->interface_name) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Deleting Dialplan '%s'\n", ptr->interface_name); if (switch_event_create(&event, SWITCH_EVENT_MODULE_UNLOAD) == SWITCH_STATUS_SUCCESS) { switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "type", "dialplan"); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "name", ptr->interface_name); switch_event_fire(&event); removed++; } switch_core_hash_delete(loadable_modules.dialplan_hash, ptr->interface_name); } } } if (old_module->module_interface->timer_interface) { const switch_timer_interface_t *ptr; for (ptr = old_module->module_interface->timer_interface; ptr; ptr = ptr->next) { if (ptr->interface_name) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Deleting Timer '%s'\n", ptr->interface_name); if (switch_event_create(&event, SWITCH_EVENT_MODULE_UNLOAD) == SWITCH_STATUS_SUCCESS) { switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "type", "timer"); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "name", ptr->interface_name); switch_event_fire(&event); removed++; } switch_core_hash_delete(loadable_modules.timer_hash, ptr->interface_name); } } } if (old_module->module_interface->application_interface) { const switch_application_interface_t *ptr; for (ptr = old_module->module_interface->application_interface; ptr; ptr = ptr->next) { if (ptr->interface_name) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Deleting Application '%s'\n", ptr->interface_name); switch_core_session_hupall_matching_var(SWITCH_CURRENT_APPLICATION_VARIABLE, ptr->interface_name, SWITCH_CAUSE_MANAGER_REQUEST); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Write lock interface '%s' to wait for existing references.\n", ptr->interface_name); if (switch_thread_rwlock_trywrlock_timeout(ptr->rwlock, 10) == SWITCH_STATUS_SUCCESS) { switch_thread_rwlock_unlock(ptr->rwlock); } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Giving up on '%s' waiting for existing references.\n", ptr->interface_name); } if (switch_event_create(&event, SWITCH_EVENT_MODULE_UNLOAD) == SWITCH_STATUS_SUCCESS) { switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "type", "application"); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "name", ptr->interface_name); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "description", switch_str_nil(ptr->short_desc)); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "syntax", switch_str_nil(ptr->syntax)); switch_event_fire(&event); removed++; } switch_core_hash_delete(loadable_modules.application_hash, ptr->interface_name); } } } if (old_module->module_interface->chat_application_interface) { const switch_chat_application_interface_t *ptr; for (ptr = old_module->module_interface->chat_application_interface; ptr; ptr = ptr->next) { if (ptr->interface_name) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Deleting Application '%s'\n", ptr->interface_name); switch_core_session_hupall_matching_var(SWITCH_CURRENT_APPLICATION_VARIABLE, ptr->interface_name, SWITCH_CAUSE_MANAGER_REQUEST); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Write lock interface '%s' to wait for existing references.\n", ptr->interface_name); if (switch_thread_rwlock_trywrlock_timeout(ptr->rwlock, 10) == SWITCH_STATUS_SUCCESS) { switch_thread_rwlock_unlock(ptr->rwlock); } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Giving up on '%s' waiting for existing references.\n", ptr->interface_name); } if (switch_event_create(&event, SWITCH_EVENT_MODULE_UNLOAD) == SWITCH_STATUS_SUCCESS) { switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "type", "application"); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "name", ptr->interface_name); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "description", switch_str_nil(ptr->short_desc)); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "syntax", switch_str_nil(ptr->syntax)); switch_event_fire(&event); removed++; } switch_core_hash_delete(loadable_modules.chat_application_hash, ptr->interface_name); } } } if (old_module->module_interface->api_interface) { const switch_api_interface_t *ptr; for (ptr = old_module->module_interface->api_interface; ptr; ptr = ptr->next) { if (ptr->interface_name) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Deleting API Function '%s'\n", ptr->interface_name); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Write lock interface '%s' to wait for existing references.\n", ptr->interface_name); if (switch_thread_rwlock_trywrlock_timeout(ptr->rwlock, 10) == SWITCH_STATUS_SUCCESS) { switch_thread_rwlock_unlock(ptr->rwlock); } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Giving up on '%s' waiting for existing references.\n", ptr->interface_name); } if (switch_event_create(&event, SWITCH_EVENT_MODULE_UNLOAD) == SWITCH_STATUS_SUCCESS) { switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "type", "api"); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "name", ptr->interface_name); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "description", switch_str_nil(ptr->desc)); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "syntax", switch_str_nil(ptr->syntax)); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "key", old_module->key); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "filename", old_module->filename); switch_event_fire(&event); removed++; } switch_core_hash_delete(loadable_modules.api_hash, ptr->interface_name); } } } if (old_module->module_interface->json_api_interface) { const switch_json_api_interface_t *ptr; for (ptr = old_module->module_interface->json_api_interface; ptr; ptr = ptr->next) { if (ptr->interface_name) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Deleting API Function '%s'\n", ptr->interface_name); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Write lock interface '%s' to wait for existing references.\n", ptr->interface_name); if (switch_thread_rwlock_trywrlock_timeout(ptr->rwlock, 10) == SWITCH_STATUS_SUCCESS) { switch_thread_rwlock_unlock(ptr->rwlock); } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Giving up on '%s' waiting for existing references.\n", ptr->interface_name); } if (switch_event_create(&event, SWITCH_EVENT_MODULE_UNLOAD) == SWITCH_STATUS_SUCCESS) { switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "type", "api"); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "name", ptr->interface_name); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "description", switch_str_nil(ptr->desc)); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "syntax", switch_str_nil(ptr->syntax)); switch_event_fire(&event); removed++; } switch_core_hash_delete(loadable_modules.json_api_hash, ptr->interface_name); } } } if (old_module->module_interface->file_interface) { const switch_file_interface_t *ptr; switch_file_node_t *node, *head, *last = NULL; for (ptr = old_module->module_interface->file_interface; ptr; ptr = ptr->next) { if (ptr->interface_name) { int i; switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Write lock interface '%s' to wait for existing references.\n", ptr->interface_name); if (switch_thread_rwlock_trywrlock_timeout(ptr->rwlock, 10) == SWITCH_STATUS_SUCCESS) { switch_thread_rwlock_unlock(ptr->rwlock); } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Giving up on '%s' waiting for existing references.\n", ptr->interface_name); } for (i = 0; ptr->extens[i]; i++) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Deleting File Format '%s'\n", ptr->extens[i]); if (switch_event_create(&event, SWITCH_EVENT_MODULE_UNLOAD) == SWITCH_STATUS_SUCCESS) { switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "type", "file"); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "name", ptr->extens[i]); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "module", old_module->module_interface->module_name); switch_event_fire(&event); removed++; } if ((head = switch_core_hash_find(loadable_modules.file_hash, ptr->extens[i]))) { for(node = head; node; node = node->next) { if (!strcmp(node->interface_name, old_module->module_interface->module_name)) { if (node == head) { if ((node = node->next)) { switch_core_hash_insert(loadable_modules.file_hash, ptr->extens[i], (const void *) node); } else { switch_core_hash_delete(loadable_modules.file_hash, ptr->extens[i]); } } else { if (last) { last->next = node->next; } } break; } last = node; } } } } } } if (old_module->module_interface->speech_interface) { const switch_speech_interface_t *ptr; for (ptr = old_module->module_interface->speech_interface; ptr; ptr = ptr->next) { if (ptr->interface_name) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Write lock interface '%s' to wait for existing references.\n", ptr->interface_name); if (switch_thread_rwlock_trywrlock_timeout(ptr->rwlock, 10) == SWITCH_STATUS_SUCCESS) { switch_thread_rwlock_unlock(ptr->rwlock); } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Giving up on '%s' waiting for existing references.\n", ptr->interface_name); } switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Deleting Speech interface '%s'\n", ptr->interface_name); if (switch_event_create(&event, SWITCH_EVENT_MODULE_UNLOAD) == SWITCH_STATUS_SUCCESS) { switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "type", "speech"); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "name", ptr->interface_name); switch_event_fire(&event); removed++; } switch_core_hash_delete(loadable_modules.speech_hash, ptr->interface_name); } } } if (old_module->module_interface->asr_interface) { const switch_asr_interface_t *ptr; for (ptr = old_module->module_interface->asr_interface; ptr; ptr = ptr->next) { if (ptr->interface_name) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Write lock interface '%s' to wait for existing references.\n", ptr->interface_name); if (switch_thread_rwlock_trywrlock_timeout(ptr->rwlock, 10) == SWITCH_STATUS_SUCCESS) { switch_thread_rwlock_unlock(ptr->rwlock); } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Giving up on '%s' waiting for existing references.\n", ptr->interface_name); } switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Deleting Asr interface '%s'\n", ptr->interface_name); if (switch_event_create(&event, SWITCH_EVENT_MODULE_UNLOAD) == SWITCH_STATUS_SUCCESS) { switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "type", "asr"); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "name", ptr->interface_name); switch_event_fire(&event); removed++; } switch_core_hash_delete(loadable_modules.asr_hash, ptr->interface_name); } } } if (old_module->module_interface->directory_interface) { const switch_directory_interface_t *ptr; for (ptr = old_module->module_interface->directory_interface; ptr; ptr = ptr->next) { if (ptr->interface_name) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Write lock interface '%s' to wait for existing references.\n", ptr->interface_name); if (switch_thread_rwlock_trywrlock_timeout(ptr->rwlock, 10) == SWITCH_STATUS_SUCCESS) { switch_thread_rwlock_unlock(ptr->rwlock); } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Giving up on '%s' waiting for existing references.\n", ptr->interface_name); } switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Deleting Directory interface '%s'\n", ptr->interface_name); if (switch_event_create(&event, SWITCH_EVENT_MODULE_UNLOAD) == SWITCH_STATUS_SUCCESS) { switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "type", "directory"); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "name", ptr->interface_name); switch_event_fire(&event); removed++; } switch_core_hash_delete(loadable_modules.directory_hash, ptr->interface_name); } } } if (old_module->module_interface->chat_interface) { const switch_chat_interface_t *ptr; for (ptr = old_module->module_interface->chat_interface; ptr; ptr = ptr->next) { if (ptr->interface_name) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Write lock interface '%s' to wait for existing references.\n", ptr->interface_name); if (switch_thread_rwlock_trywrlock_timeout(ptr->rwlock, 10) == SWITCH_STATUS_SUCCESS) { switch_thread_rwlock_unlock(ptr->rwlock); } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Giving up on '%s' waiting for existing references.\n", ptr->interface_name); } switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Deleting Chat interface '%s'\n", ptr->interface_name); if (switch_event_create(&event, SWITCH_EVENT_MODULE_UNLOAD) == SWITCH_STATUS_SUCCESS) { switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "type", "chat"); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "name", ptr->interface_name); switch_event_fire(&event); removed++; } switch_core_hash_delete(loadable_modules.chat_hash, ptr->interface_name); } } } if (old_module->module_interface->say_interface) { const switch_say_interface_t *ptr; for (ptr = old_module->module_interface->say_interface; ptr; ptr = ptr->next) { if (ptr->interface_name) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Write lock interface '%s' to wait for existing references.\n", ptr->interface_name); if (switch_thread_rwlock_trywrlock_timeout(ptr->rwlock, 10) == SWITCH_STATUS_SUCCESS) { switch_thread_rwlock_unlock(ptr->rwlock); } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Giving up on '%s' waiting for existing references.\n", ptr->interface_name); } switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Deleting Say interface '%s'\n", ptr->interface_name); if (switch_event_create(&event, SWITCH_EVENT_MODULE_UNLOAD) == SWITCH_STATUS_SUCCESS) { switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "type", "say"); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "name", ptr->interface_name); switch_event_fire(&event); removed++; } switch_core_hash_delete(loadable_modules.say_hash, ptr->interface_name); } } } if (old_module->module_interface->management_interface) { const switch_management_interface_t *ptr; for (ptr = old_module->module_interface->management_interface; ptr; ptr = ptr->next) { if (ptr->relative_oid) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Deleting Management interface '%s' OID[%s.%s]\n", old_module->key, FREESWITCH_OID_PREFIX, ptr->relative_oid); switch_core_hash_delete(loadable_modules.management_hash, ptr->relative_oid); if (switch_event_create(&event, SWITCH_EVENT_MODULE_UNLOAD) == SWITCH_STATUS_SUCCESS) { switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "type", "management"); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "name", ptr->relative_oid); switch_event_fire(&event); removed++; } } } } if (old_module->module_interface->limit_interface) { const switch_limit_interface_t *ptr; for (ptr = old_module->module_interface->limit_interface; ptr; ptr = ptr->next) { if (ptr->interface_name) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Deleting Limit interface '%s'\n", ptr->interface_name); switch_core_hash_delete(loadable_modules.limit_hash, ptr->interface_name); if (switch_event_create(&event, SWITCH_EVENT_MODULE_UNLOAD) == SWITCH_STATUS_SUCCESS) { switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "type", "limit"); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "name", ptr->interface_name); switch_event_fire(&event); removed++; } } } } if (!removed) { if (switch_event_create(&event, SWITCH_EVENT_MODULE_UNLOAD) == SWITCH_STATUS_SUCCESS) { switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "type", "generic"); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "name", old_module->key); switch_event_fire(&event); removed++; } } switch_mutex_unlock(loadable_modules.mutex); return SWITCH_STATUS_SUCCESS; } static switch_status_t switch_loadable_module_load_file(char *path, char *filename, switch_bool_t global, switch_loadable_module_t **new_module) { switch_loadable_module_t *module = NULL; switch_dso_lib_t dso = NULL; apr_status_t status = SWITCH_STATUS_SUCCESS; switch_loadable_module_function_table_t *interface_struct_handle = NULL; switch_loadable_module_function_table_t *mod_interface_functions = NULL; char *struct_name = NULL; switch_module_load_t load_func_ptr = NULL; int loading = 1; switch_loadable_module_interface_t *module_interface = NULL; char *derr = NULL; const char *err = NULL; switch_memory_pool_t *pool = NULL; switch_bool_t load_global = global; switch_assert(path != NULL); switch_core_new_memory_pool(&pool); *new_module = NULL; struct_name = switch_core_sprintf(pool, "%s_module_interface", filename); #ifdef WIN32 dso = switch_dso_open("FreeSwitch.dll", load_global, &derr); #elif defined (MACOSX) || defined(DARWIN) { char *lib_path = switch_mprintf("%s/libfreeswitch.dylib", SWITCH_GLOBAL_dirs.lib_dir); dso = switch_dso_open(lib_path, load_global, &derr); switch_safe_free(lib_path); } #else dso = switch_dso_open(NULL, load_global, &derr); #endif if (!derr && dso) { interface_struct_handle = switch_dso_data_sym(dso, struct_name, &derr); } switch_safe_free(derr); if (!interface_struct_handle) { if (dso) switch_dso_destroy(&dso); dso = switch_dso_open(path, load_global, &derr); } while (loading) { if (derr) { err = derr; break; } if (!interface_struct_handle) { interface_struct_handle = switch_dso_data_sym(dso, struct_name, &derr); } if (derr) { err = derr; break; } if (interface_struct_handle && interface_struct_handle->switch_api_version != SWITCH_API_VERSION) { err = "Trying to load an out of date module, please rebuild the module."; break; } if (!load_global && interface_struct_handle && switch_test_flag(interface_struct_handle, SMODF_GLOBAL_SYMBOLS)) { load_global = SWITCH_TRUE; switch_dso_destroy(&dso); interface_struct_handle = NULL; dso = switch_dso_open(path, load_global, &derr); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Loading module with global namespace at request of module\n"); continue; } if (interface_struct_handle) { mod_interface_functions = interface_struct_handle; load_func_ptr = mod_interface_functions->load; } if (load_func_ptr == NULL) { err = "Cannot locate symbol 'switch_module_load' please make sure this is a valid module."; break; } status = load_func_ptr(&module_interface, pool); if (status != SWITCH_STATUS_SUCCESS && status != SWITCH_STATUS_NOUNLOAD) { err = "Module load routine returned an error"; module_interface = NULL; break; } if (!module_interface) { err = "Module failed to initialize its module_interface. Is this a valid module?"; break; } if ((module = switch_core_alloc(pool, sizeof(switch_loadable_module_t))) == 0) { err = "Could not allocate memory\n"; abort(); } if (status == SWITCH_STATUS_NOUNLOAD) { module->perm++; } loading = 0; } if (err) { if (dso) { switch_dso_destroy(&dso); } if (pool) { switch_core_destroy_memory_pool(&pool); } switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Error Loading module %s\n**%s**\n", path, err); switch_safe_free(derr); return SWITCH_STATUS_GENERR; } module->pool = pool; module->filename = switch_core_strdup(module->pool, path); module->module_interface = module_interface; module->switch_module_load = load_func_ptr; if (mod_interface_functions) { module->switch_module_shutdown = mod_interface_functions->shutdown; module->switch_module_runtime = mod_interface_functions->runtime; } module->lib = dso; *new_module = module; switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Successfully Loaded [%s]\n", module_interface->module_name); switch_core_set_signal_handlers(); return SWITCH_STATUS_SUCCESS; } SWITCH_DECLARE(switch_status_t) switch_loadable_module_load_module(char *dir, char *fname, switch_bool_t runtime, const char **err) { return switch_loadable_module_load_module_ex(dir, fname, runtime, SWITCH_FALSE, err); } static switch_status_t switch_loadable_module_load_module_ex(char *dir, char *fname, switch_bool_t runtime, switch_bool_t global, const char **err) { switch_size_t len = 0; char *path; char *file, *dot; switch_loadable_module_t *new_module = NULL; switch_status_t status = SWITCH_STATUS_SUCCESS; #ifdef WIN32 const char *ext = ".dll"; #else const char *ext = ".so"; #endif *err = ""; if ((file = switch_core_strdup(loadable_modules.pool, fname)) == 0) { *err = "allocation error"; return SWITCH_STATUS_FALSE; } if (switch_is_file_path(file)) { path = switch_core_strdup(loadable_modules.pool, file); file = (char *) switch_cut_path(file); if ((dot = strchr(file, '.'))) { *dot = '\0'; } } else { if ((dot = strchr(file, '.'))) { *dot = '\0'; } len = strlen(dir); len += strlen(file); len += 8; path = (char *) switch_core_alloc(loadable_modules.pool, len); switch_snprintf(path, len, "%s%s%s%s", dir, SWITCH_PATH_SEPARATOR, file, ext); } if (switch_core_hash_find_locked(loadable_modules.module_hash, file, loadable_modules.mutex)) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Module %s Already Loaded!\n", file); *err = "Module already loaded"; status = SWITCH_STATUS_FALSE; } else if ((status = switch_loadable_module_load_file(path, file, global, &new_module)) == SWITCH_STATUS_SUCCESS) { if ((status = switch_loadable_module_process(file, new_module)) == SWITCH_STATUS_SUCCESS && runtime) { if (new_module->switch_module_runtime) { new_module->thread = switch_core_launch_thread(switch_loadable_module_exec, new_module, new_module->pool); } } else if (status != SWITCH_STATUS_SUCCESS) { *err = "module load routine returned an error"; } } else { *err = "module load file routine returned an error"; } return status; } SWITCH_DECLARE(switch_status_t) switch_loadable_module_exists(const char *mod) { switch_status_t status; if (zstr(mod)) { return SWITCH_STATUS_FALSE; } switch_mutex_lock(loadable_modules.mutex); if (switch_core_hash_find(loadable_modules.module_hash, mod)) { status = SWITCH_STATUS_SUCCESS; } else { status = SWITCH_STATUS_FALSE; } switch_mutex_unlock(loadable_modules.mutex); return status; } SWITCH_DECLARE(switch_status_t) switch_loadable_module_unload_module(char *dir, char *fname, switch_bool_t force, const char **err) { switch_loadable_module_t *module = NULL; switch_status_t status = SWITCH_STATUS_SUCCESS; if (force) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Spin the barrel and pull the trigger.......!\n"); } switch_mutex_lock(loadable_modules.mutex); if ((module = switch_core_hash_find(loadable_modules.module_hash, fname))) { if (module->perm) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Module is not unloadable.\n"); *err = "Module is not unloadable"; status = SWITCH_STATUS_NOUNLOAD; goto unlock; } else { /* Prevent anything from using the module while it's shutting down */ switch_core_hash_delete(loadable_modules.module_hash, fname); switch_mutex_unlock(loadable_modules.mutex); if ((status = do_shutdown(module, SWITCH_TRUE, SWITCH_TRUE, !force, err)) != SWITCH_STATUS_SUCCESS) { /* Something went wrong in the module's shutdown function, add it again */ switch_core_hash_insert_locked(loadable_modules.module_hash, fname, module, loadable_modules.mutex); } goto end; } } else { *err = "No such module!"; status = SWITCH_STATUS_FALSE; } unlock: switch_mutex_unlock(loadable_modules.mutex); end: if (force) { switch_yield(1000000); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "PHEW!\n"); } return status; } SWITCH_DECLARE(switch_status_t) switch_loadable_module_enumerate_available(const char *dir_path, switch_modulename_callback_func_t callback, void *user_data) { switch_dir_t *dir = NULL; switch_status_t status; char buffer[256]; const char *fname; const char *fname_ext; char *fname_base; #ifdef WIN32 const char *ext = ".dll"; #else const char *ext = ".so"; #endif if ((status = switch_dir_open(&dir, dir_path, loadable_modules.pool)) != SWITCH_STATUS_SUCCESS) { return status; } while((fname = switch_dir_next_file(dir, buffer, sizeof(buffer)))) { if ((fname_ext = strrchr(fname, '.'))) { if (!strcmp(fname_ext, ext)) { if (!(fname_base = switch_mprintf("%.*s", (int)(fname_ext-fname), fname))) { status = SWITCH_STATUS_GENERR; goto end; } callback(user_data, fname_base); switch_safe_free(fname_base) } } } end: switch_dir_close(dir); return status; } SWITCH_DECLARE(switch_status_t) switch_loadable_module_enumerate_loaded(switch_modulename_callback_func_t callback, void *user_data) { switch_hash_index_t *hi; void *val; switch_loadable_module_t *module; switch_mutex_lock(loadable_modules.mutex); for (hi = switch_core_hash_first(loadable_modules.module_hash); hi; hi = switch_core_hash_next(&hi)) { switch_core_hash_this(hi, NULL, NULL, &val); module = (switch_loadable_module_t *) val; callback(user_data, module->module_interface->module_name); } switch_mutex_unlock(loadable_modules.mutex); return SWITCH_STATUS_SUCCESS; } SWITCH_DECLARE(switch_status_t) switch_loadable_module_build_dynamic(char *filename, switch_module_load_t switch_module_load, switch_module_runtime_t switch_module_runtime, switch_module_shutdown_t switch_module_shutdown, switch_bool_t runtime) { switch_loadable_module_t *module = NULL; switch_module_load_t load_func_ptr = NULL; int loading = 1; const char *err = NULL; switch_loadable_module_interface_t *module_interface = NULL; switch_memory_pool_t *pool; if (switch_core_new_memory_pool(&pool) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "OH OH no pool\n"); abort(); } if ((module = switch_core_alloc(pool, sizeof(switch_loadable_module_t))) == 0) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Couldn't allocate memory\n"); abort(); } while (loading) { switch_status_t status; load_func_ptr = (switch_module_load_t) switch_module_load; if (load_func_ptr == NULL) { err = "Cannot Load"; break; } status = load_func_ptr(&module_interface, pool); if (status != SWITCH_STATUS_SUCCESS && status != SWITCH_STATUS_NOUNLOAD) { err = "Module load routine returned an error"; module_interface = NULL; break; } if ((module = switch_core_alloc(pool, sizeof(switch_loadable_module_t))) == 0) { err = "Could not allocate memory\n"; abort(); } if (status == SWITCH_STATUS_NOUNLOAD) { module->perm++; } loading = 0; } if (err) { switch_core_destroy_memory_pool(&pool); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Error Loading module %s\n**%s**\n", filename, err); return SWITCH_STATUS_GENERR; } module->pool = pool; module->filename = switch_core_strdup(module->pool, filename); module->module_interface = module_interface; module->switch_module_load = load_func_ptr; if (switch_module_shutdown) { module->switch_module_shutdown = switch_module_shutdown; } if (switch_module_runtime) { module->switch_module_runtime = switch_module_runtime; } if (runtime && module->switch_module_runtime) { module->thread = switch_core_launch_thread(switch_loadable_module_exec, module, module->pool); } switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Successfully Loaded [%s]\n", module_interface->module_name); return switch_loadable_module_process((char *) module->filename, module); } #ifdef WIN32 static void switch_loadable_module_path_init() { char *path = NULL, *working = NULL; apr_dir_t *perl_dir_handle = NULL; apr_env_get(&path, "path", loadable_modules.pool); apr_filepath_get(&working, APR_FILEPATH_NATIVE, loadable_modules.pool); if (apr_dir_open(&perl_dir_handle, ".\\perl", loadable_modules.pool) == APR_SUCCESS) { apr_dir_close(perl_dir_handle); apr_env_set("path", apr_pstrcat(loadable_modules.pool, path, ";", working, "\\perl", NULL), loadable_modules.pool); } } #endif SWITCH_DECLARE(switch_status_t) switch_loadable_module_init(switch_bool_t autoload) { apr_finfo_t finfo = { 0 }; apr_dir_t *module_dir_handle = NULL; apr_int32_t finfo_flags = APR_FINFO_DIRENT | APR_FINFO_TYPE | APR_FINFO_NAME; char *cf = "modules.conf"; char *pcf = "post_load_modules.conf"; switch_xml_t cfg, xml; unsigned char all = 0; unsigned int count = 0; const char *err; #ifdef WIN32 const char *ext = ".dll"; const char *EXT = ".DLL"; #elif defined (MACOSX) || defined (DARWIN) const char *ext = ".dylib"; const char *EXT = ".DYLIB"; #else const char *ext = ".so"; const char *EXT = ".SO"; #endif memset(&loadable_modules, 0, sizeof(loadable_modules)); switch_core_new_memory_pool(&loadable_modules.pool); #ifdef WIN32 switch_loadable_module_path_init(); #endif switch_core_hash_init(&loadable_modules.module_hash); switch_core_hash_init_nocase(&loadable_modules.endpoint_hash); switch_core_hash_init_nocase(&loadable_modules.codec_hash); switch_core_hash_init_nocase(&loadable_modules.timer_hash); switch_core_hash_init_nocase(&loadable_modules.application_hash); switch_core_hash_init_nocase(&loadable_modules.chat_application_hash); switch_core_hash_init_nocase(&loadable_modules.api_hash); switch_core_hash_init_nocase(&loadable_modules.json_api_hash); switch_core_hash_init(&loadable_modules.file_hash); switch_core_hash_init_nocase(&loadable_modules.speech_hash); switch_core_hash_init_nocase(&loadable_modules.asr_hash); switch_core_hash_init_nocase(&loadable_modules.directory_hash); switch_core_hash_init_nocase(&loadable_modules.chat_hash); switch_core_hash_init_nocase(&loadable_modules.say_hash); switch_core_hash_init_nocase(&loadable_modules.management_hash); switch_core_hash_init_nocase(&loadable_modules.limit_hash); switch_core_hash_init_nocase(&loadable_modules.dialplan_hash); switch_core_hash_init(&loadable_modules.secondary_recover_hash); switch_mutex_init(&loadable_modules.mutex, SWITCH_MUTEX_NESTED, loadable_modules.pool); if (!autoload) return SWITCH_STATUS_SUCCESS; switch_loadable_module_load_module("", "CORE_SOFTTIMER_MODULE", SWITCH_FALSE, &err); switch_loadable_module_load_module("", "CORE_PCM_MODULE", SWITCH_FALSE, &err); switch_loadable_module_load_module("", "CORE_SPEEX_MODULE", SWITCH_FALSE, &err); #ifdef SWITCH_HAVE_YUV #ifdef SWITCH_HAVE_VPX switch_loadable_module_load_module("", "CORE_VPX_MODULE", SWITCH_FALSE, &err); #endif #endif if ((xml = switch_xml_open_cfg(cf, &cfg, NULL))) { switch_xml_t mods, ld; if ((mods = switch_xml_child(cfg, "modules"))) { for (ld = switch_xml_child(mods, "load"); ld; ld = ld->next) { switch_bool_t global = SWITCH_FALSE; const char *val = switch_xml_attr_soft(ld, "module"); const char *path = switch_xml_attr_soft(ld, "path"); const char *critical = switch_xml_attr_soft(ld, "critical"); const char *sglobal = switch_xml_attr_soft(ld, "global"); if (zstr(val) || (strchr(val, '.') && !strstr(val, ext) && !strstr(val, EXT))) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Invalid extension for %s\n", val); continue; } global = switch_true(sglobal); if (path && zstr(path)) { path = SWITCH_GLOBAL_dirs.mod_dir; } if (switch_loadable_module_load_module_ex((char *) path, (char *) val, SWITCH_FALSE, global, &err) == SWITCH_STATUS_GENERR) { if (critical && switch_true(critical)) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to load critical module '%s', abort()\n", val); abort(); } } count++; } } switch_xml_free(xml); } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "open of %s failed\n", cf); } if ((xml = switch_xml_open_cfg(pcf, &cfg, NULL))) { switch_xml_t mods, ld; if ((mods = switch_xml_child(cfg, "modules"))) { for (ld = switch_xml_child(mods, "load"); ld; ld = ld->next) { switch_bool_t global = SWITCH_FALSE; const char *val = switch_xml_attr_soft(ld, "module"); const char *path = switch_xml_attr_soft(ld, "path"); const char *sglobal = switch_xml_attr_soft(ld, "global"); if (zstr(val) || (strchr(val, '.') && !strstr(val, ext) && !strstr(val, EXT))) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Invalid extension for %s\n", val); continue; } global = switch_true(sglobal); if (path && zstr(path)) { path = SWITCH_GLOBAL_dirs.mod_dir; } switch_loadable_module_load_module_ex((char *) path, (char *) val, SWITCH_FALSE, global, &err); count++; } } switch_xml_free(xml); } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "open of %s failed\n", pcf); } if (!count) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "No modules loaded, assuming 'load all'\n"); all = 1; } if (all) { if (apr_dir_open(&module_dir_handle, SWITCH_GLOBAL_dirs.mod_dir, loadable_modules.pool) != APR_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Can't open directory: %s\n", SWITCH_GLOBAL_dirs.mod_dir); return SWITCH_STATUS_GENERR; } while (apr_dir_read(&finfo, finfo_flags, module_dir_handle) == APR_SUCCESS) { const char *fname = finfo.fname; if (finfo.filetype != APR_REG) { continue; } if (!fname) { fname = finfo.name; } if (!fname) { continue; } if (zstr(fname) || (!strstr(fname, ext) && !strstr(fname, EXT))) { continue; } switch_loadable_module_load_module((char *) SWITCH_GLOBAL_dirs.mod_dir, (char *) fname, SWITCH_FALSE, &err); } apr_dir_close(module_dir_handle); } switch_loadable_module_runtime(); memset(&chat_globals, 0, sizeof(chat_globals)); chat_globals.running = 1; chat_globals.pool = loadable_modules.pool; switch_mutex_init(&chat_globals.mutex, SWITCH_MUTEX_NESTED, chat_globals.pool); chat_thread_start(1); return SWITCH_STATUS_SUCCESS; } static switch_status_t do_shutdown(switch_loadable_module_t *module, switch_bool_t shutdown, switch_bool_t unload, switch_bool_t fail_if_busy, const char **err) { int32_t flags = switch_core_flags(); switch_assert(module != NULL); if (fail_if_busy && module->module_interface->rwlock && switch_thread_rwlock_trywrlock(module->module_interface->rwlock) != SWITCH_STATUS_SUCCESS) { if (err) { *err = "Module in use."; } switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Module %s is in use, cannot unload.\n", module->module_interface->module_name); return SWITCH_STATUS_FALSE; } module->shutting_down = SWITCH_TRUE; if (shutdown) { switch_loadable_module_unprocess(module); if (module->switch_module_shutdown) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Stopping: %s\n", module->module_interface->module_name); module->status = module->switch_module_shutdown(); } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "%s has no shutdown routine\n", module->module_interface->module_name); } } if (fail_if_busy && module->module_interface->rwlock) { switch_thread_rwlock_unlock(module->module_interface->rwlock); } if (unload && module->status != SWITCH_STATUS_NOUNLOAD && !(flags & SCF_VG)) { switch_memory_pool_t *pool; switch_status_t st; if (module->thread) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "%s stopping runtime thread.\n", module->module_interface->module_name); switch_thread_join(&st, module->thread); } switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "%s unloaded.\n", module->module_interface->module_name); switch_dso_destroy(&module->lib); if ((pool = module->pool)) { module = NULL; switch_core_destroy_memory_pool(&pool); } } return SWITCH_STATUS_SUCCESS; } SWITCH_DECLARE(void) switch_loadable_module_shutdown(void) { switch_hash_index_t *hi; void *val; switch_loadable_module_t *module; int i; if (!loadable_modules.module_hash) { return; } chat_globals.running = 0; for (i = 0; i < chat_globals.msg_queue_len; i++) { switch_queue_push(chat_globals.msg_queue[i], NULL); } for (i = 0; i < chat_globals.msg_queue_len; i++) { switch_status_t st; switch_thread_join(&st, chat_globals.msg_queue_thread[i]); } for (hi = switch_core_hash_first(loadable_modules.module_hash); hi; hi = switch_core_hash_next(&hi)) { switch_core_hash_this(hi, NULL, NULL, &val); module = (switch_loadable_module_t *) val; if (!module->perm) { do_shutdown(module, SWITCH_TRUE, SWITCH_FALSE, SWITCH_FALSE, NULL); } } switch_yield(1000000); for (hi = switch_core_hash_first(loadable_modules.module_hash); hi; hi = switch_core_hash_next(&hi)) { switch_core_hash_this(hi, NULL, NULL, &val); module = (switch_loadable_module_t *) val; if (!module->perm) { do_shutdown(module, SWITCH_FALSE, SWITCH_TRUE, SWITCH_FALSE, NULL); } } switch_core_hash_destroy(&loadable_modules.module_hash); switch_core_hash_destroy(&loadable_modules.endpoint_hash); switch_core_hash_destroy(&loadable_modules.codec_hash); switch_core_hash_destroy(&loadable_modules.timer_hash); switch_core_hash_destroy(&loadable_modules.application_hash); switch_core_hash_destroy(&loadable_modules.chat_application_hash); switch_core_hash_destroy(&loadable_modules.api_hash); switch_core_hash_destroy(&loadable_modules.json_api_hash); switch_core_hash_destroy(&loadable_modules.file_hash); switch_core_hash_destroy(&loadable_modules.speech_hash); switch_core_hash_destroy(&loadable_modules.asr_hash); switch_core_hash_destroy(&loadable_modules.directory_hash); switch_core_hash_destroy(&loadable_modules.chat_hash); switch_core_hash_destroy(&loadable_modules.say_hash); switch_core_hash_destroy(&loadable_modules.management_hash); switch_core_hash_destroy(&loadable_modules.limit_hash); switch_core_hash_destroy(&loadable_modules.dialplan_hash); switch_core_destroy_memory_pool(&loadable_modules.pool); } SWITCH_DECLARE(switch_endpoint_interface_t *) switch_loadable_module_get_endpoint_interface(const char *name) { switch_endpoint_interface_t *ptr; switch_mutex_lock(loadable_modules.mutex); ptr = switch_core_hash_find(loadable_modules.endpoint_hash, name); if (ptr) { PROTECT_INTERFACE(ptr); } switch_mutex_unlock(loadable_modules.mutex); return ptr; } SWITCH_DECLARE(switch_file_interface_t *) switch_loadable_module_get_file_interface(const char *name, const char *modname) { switch_file_interface_t *i = NULL; switch_file_node_t *node, *head; switch_mutex_lock(loadable_modules.mutex); if ((head = switch_core_hash_find(loadable_modules.file_hash, name))) { if (modname) { for (node = head; node; node = node->next) { if (!strcasecmp(node->interface_name, modname)) { i = (switch_file_interface_t *) node->ptr; break; } } } else { i = (switch_file_interface_t *) head->ptr; } } switch_mutex_unlock(loadable_modules.mutex); if (i) PROTECT_INTERFACE(i); return i; } SWITCH_DECLARE(switch_codec_interface_t *) switch_loadable_module_get_codec_interface(const char *name, const char *modname) { switch_codec_interface_t *codec = NULL; switch_codec_node_t *node, *head; switch_mutex_lock(loadable_modules.mutex); if ((head = switch_core_hash_find(loadable_modules.codec_hash, name))) { if (modname) { for (node = head; node; node = node->next) { if (!strcasecmp(node->interface_name, modname)) { codec = (switch_codec_interface_t *) node->ptr; break; } } } else { codec = (switch_codec_interface_t *) head->ptr; } } switch_mutex_unlock(loadable_modules.mutex); if (codec) { PROTECT_INTERFACE(codec); } return codec; } #define HASH_FUNC(_kind_) SWITCH_DECLARE(switch_##_kind_##_interface_t *) switch_loadable_module_get_##_kind_##_interface(const char *name) \ { \ switch_##_kind_##_interface_t *i = NULL; \ if (loadable_modules._kind_##_hash && (i = switch_core_hash_find_locked(loadable_modules._kind_##_hash, name, loadable_modules.mutex))) { \ PROTECT_INTERFACE(i); \ } \ return i; \ } HASH_FUNC(dialplan) HASH_FUNC(timer) HASH_FUNC(application) HASH_FUNC(chat_application) HASH_FUNC(api) HASH_FUNC(json_api) HASH_FUNC(speech) HASH_FUNC(asr) HASH_FUNC(directory) HASH_FUNC(chat) HASH_FUNC(limit) SWITCH_DECLARE(switch_say_interface_t *) switch_loadable_module_get_say_interface(const char *name) { return switch_core_hash_find_locked(loadable_modules.say_hash, name, loadable_modules.mutex); } SWITCH_DECLARE(switch_management_interface_t *) switch_loadable_module_get_management_interface(const char *relative_oid) { return switch_core_hash_find_locked(loadable_modules.management_hash, relative_oid, loadable_modules.mutex); } #ifdef DEBUG_CODEC_SORTING static void do_print(const switch_codec_implementation_t **array, int arraylen) { int i; for(i = 0; i < arraylen; i++) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "DEBUG %d %s:%d %d\n", i, array[i]->iananame, array[i]->ianacode, array[i]->microseconds_per_packet / 1000); } } #endif /* helper only -- bounds checking enforced by caller */ static void do_swap(const switch_codec_implementation_t **array, int a, int b) { const switch_codec_implementation_t *tmp = array[b]; array[b] = array[a]; array[a] = tmp; } static void switch_loadable_module_sort_codecs(const switch_codec_implementation_t **array, int arraylen) { int i = 0, sorted_ptime = 0; #ifdef DEBUG_CODEC_SORTING switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "--BEFORE\n"); do_print(array, arraylen); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "--BEFORE\n"); #endif for (i = 0; i < arraylen; i++) { int this_ptime = array[i]->microseconds_per_packet / 1000; if (!strcasecmp(array[i]->iananame, "ilbc")) { this_ptime = 20; } if (!sorted_ptime) { sorted_ptime = this_ptime; #ifdef DEBUG_CODEC_SORTING switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "sorted1 = %d\n", sorted_ptime); #endif } if (i > 0 && strcasecmp(array[i]->iananame, array[i-1]->iananame) && this_ptime != sorted_ptime) { int j; int swapped = 0; #ifdef DEBUG_CODEC_SORTING switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "%d != %d\n", this_ptime, sorted_ptime); #endif for(j = i; j < arraylen; j++) { int check_ptime = array[j]->microseconds_per_packet / 1000; if (!strcasecmp(array[i]->iananame, "ilbc")) { check_ptime = 20; } if (check_ptime == sorted_ptime) { #ifdef DEBUG_CODEC_SORTING switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "swap %d %d ptime %d\n", i, j, check_ptime); #endif do_swap(array, i, j); swapped = 1; break; } } if (!swapped) { sorted_ptime = this_ptime; #ifdef DEBUG_CODEC_SORTING switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "sorted2 = %d\n", sorted_ptime); #endif } } } #ifdef DEBUG_CODEC_SORTING switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "--AFTER\n"); do_print(array, arraylen); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "--AFTER\n"); #endif } SWITCH_DECLARE(int) switch_loadable_module_get_codecs(const switch_codec_implementation_t **array, int arraylen) { switch_hash_index_t *hi; void *val; switch_codec_interface_t *codec_interface; int i = 0; const switch_codec_implementation_t *imp; switch_codec_node_t *node, *head; switch_mutex_lock(loadable_modules.mutex); for (hi = switch_core_hash_first(loadable_modules.codec_hash); hi; hi = switch_core_hash_next(&hi)) { switch_core_hash_this(hi, NULL, NULL, &val); head = (switch_codec_node_t *) val; for (node = head; node; node = node->next) { codec_interface = (switch_codec_interface_t *) node->ptr; /* Look for the default ptime of the codec because it's the safest choice */ for (imp = codec_interface->implementations; imp; imp = imp->next) { uint32_t default_ptime = switch_default_ptime(imp->iananame, imp->ianacode); if (imp->microseconds_per_packet / 1000 == (int)default_ptime) { array[i++] = imp; goto found; } } /* oh well we will use what we have */ array[i++] = codec_interface->implementations; } found: if (i > arraylen) { break; } } switch_safe_free(hi); switch_mutex_unlock(loadable_modules.mutex); switch_loadable_module_sort_codecs(array, i); return i; } SWITCH_DECLARE(char *) switch_parse_codec_buf(char *buf, uint32_t *interval, uint32_t *rate, uint32_t *bit, uint32_t *channels, char **modname, char **fmtp) { char *cur, *next = NULL, *name, *p; name = next = cur = buf; *channels = 1; for (;;) { if (!next) { break; } if ((p = strchr(next, '@'))) { *p++ = '\0'; } next = p; if (cur != name) { if (strchr(cur, 'i')) { *interval = atoi(cur); } else if ((strchr(cur, 'k') || strchr(cur, 'h'))) { *rate = atoi(cur); } else if (strchr(cur, 'b')) { *bit = atoi(cur); } else if (strchr(cur, 'c')) { *channels = atoi(cur); } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Bad syntax for codec string. Missing qualifier [h|k|i|b|c] for part [%s]!\n", cur); } } cur = next; } if ((p = strchr(name, '.'))) { *p++ = '\0'; *modname = name; name = p; } if ((p = strchr(name, '~'))) { *p++ = '\0'; if (fmtp) { *fmtp = p; } } return name; } SWITCH_DECLARE(int) switch_loadable_module_get_codecs_sorted(const switch_codec_implementation_t **array, char fmtp_array[SWITCH_MAX_CODECS][MAX_FMTP_LEN], int arraylen, char **prefs, int preflen) { int x, i = 0, j = 0; switch_codec_interface_t *codec_interface; const switch_codec_implementation_t *imp; switch_mutex_lock(loadable_modules.mutex); for (x = 0; x < preflen; x++) { char *name, buf[256], jbuf[256], *modname = NULL, *fmtp = NULL; uint32_t interval = 0, rate = 0, bit = 0, channels = 1; switch_copy_string(buf, prefs[x], sizeof(buf)); name = switch_parse_codec_buf(buf, &interval, &rate, &bit, &channels, &modname, &fmtp); for(j = 0; j < x; j++) { char *jname, *jmodname = NULL, *jfmtp = NULL; uint32_t jinterval = 0, jrate = 0, jbit = 0, jchannels = 1; uint32_t ointerval = interval, orate = rate, ochannels = channels; if (ointerval == 0) { ointerval = switch_default_ptime(name, 0); } if (orate == 0) { orate = switch_default_rate(name, 0); } if (ochannels == 0) { ochannels = 1; } switch_copy_string(jbuf, prefs[j], sizeof(jbuf)); jname = switch_parse_codec_buf(jbuf, &jinterval, &jrate, &jbit, &jchannels, &jmodname, &jfmtp); if (jinterval == 0) { jinterval = switch_default_ptime(jname, 0); } if (jrate == 0) { jrate = switch_default_rate(jname, 0); } if (jchannels == 0) { jchannels = 1; } if (!strcasecmp(name, jname) && ointerval == jinterval && orate == jrate && ochannels == jchannels && !strcasecmp(switch_str_nil(fmtp), switch_str_nil(jfmtp))) { goto next_x; } } if ((codec_interface = switch_loadable_module_get_codec_interface(name, modname)) != 0) { /* If no specific codec interval is requested opt for the default above all else because lots of stuff assumes it */ for (imp = codec_interface->implementations; imp; imp = imp->next) { uint32_t default_ptime = switch_default_ptime(imp->iananame, imp->ianacode); uint32_t default_rate = switch_default_rate(imp->iananame, imp->ianacode); if (imp->codec_type != SWITCH_CODEC_TYPE_VIDEO) { uint32_t crate = !strcasecmp(imp->iananame, "g722") ? imp->samples_per_second : imp->actual_samples_per_second; if ((!interval && (uint32_t) (imp->microseconds_per_packet / 1000) != default_ptime) || (interval && (uint32_t) (imp->microseconds_per_packet / 1000) != interval)) { continue; } if (((!rate && crate != default_rate) || (rate && (uint32_t) imp->actual_samples_per_second != rate))) { continue; } if (bit && (uint32_t) imp->bits_per_second != bit) { continue; } if (channels && imp->number_of_channels != channels) { continue; } } if (!zstr(fmtp)) { switch_set_string(fmtp_array[i], fmtp); } array[i++] = imp; goto found; } /* Either looking for a specific interval or there was no interval specified and there wasn't one at the default ptime available */ for (imp = codec_interface->implementations; imp; imp = imp->next) { if (imp->codec_type != SWITCH_CODEC_TYPE_VIDEO) { uint32_t crate = !strcasecmp(imp->iananame, "g722") ? imp->samples_per_second : imp->actual_samples_per_second; if (interval && (uint32_t) (imp->microseconds_per_packet / 1000) != interval) { continue; } if (rate && (uint32_t) crate != rate) { continue; } if (bit && (uint32_t) imp->bits_per_second != bit) { continue; } if (channels && imp->number_of_channels != channels) { continue; } } array[i++] = imp; goto found; } found: UNPROTECT_INTERFACE(codec_interface); if (i > arraylen) { break; } } next_x: continue; } switch_mutex_unlock(loadable_modules.mutex); switch_loadable_module_sort_codecs(array, i); return i; } SWITCH_DECLARE(switch_status_t) switch_api_execute(const char *cmd, const char *arg, switch_core_session_t *session, switch_stream_handle_t *stream) { switch_api_interface_t *api; switch_status_t status; char *arg_used; char *cmd_used; switch_assert(stream != NULL); switch_assert(stream->data != NULL); switch_assert(stream->write_function != NULL); if (strcasecmp(cmd, "console_complete")) { cmd_used = switch_strip_whitespace(cmd); arg_used = switch_strip_whitespace(arg); } else { cmd_used = (char *) cmd; arg_used = (char *) arg; } if (!stream->param_event) { switch_event_create(&stream->param_event, SWITCH_EVENT_API); } if (stream->param_event) { if (cmd_used && *cmd_used) { switch_event_add_header_string(stream->param_event, SWITCH_STACK_BOTTOM, "API-Command", cmd_used); } if (arg_used && *arg_used) { switch_event_add_header_string(stream->param_event, SWITCH_STACK_BOTTOM, "API-Command-Argument", arg_used); } } if (cmd_used && (api = switch_loadable_module_get_api_interface(cmd_used)) != 0) { if ((status = api->function(arg_used, session, stream)) != SWITCH_STATUS_SUCCESS) { stream->write_function(stream, "COMMAND RETURNED ERROR!\n"); } UNPROTECT_INTERFACE(api); } else { status = SWITCH_STATUS_FALSE; stream->write_function(stream, "INVALID COMMAND!\n"); } if (stream->param_event) { switch_event_fire(&stream->param_event); } if (cmd_used != cmd) { switch_safe_free(cmd_used); } if (arg_used != arg) { switch_safe_free(arg_used); } return status; } SWITCH_DECLARE(switch_status_t) switch_json_api_execute(cJSON *json, switch_core_session_t *session, cJSON **retval) { switch_json_api_interface_t *json_api; switch_status_t status; cJSON *function, *json_reply = NULL; switch_assert(json); function = cJSON_GetObjectItem(json, "command"); if (function && function->valuestring && cJSON_GetObjectItem(json, "data") && (json_api = switch_loadable_module_get_json_api_interface(function->valuestring)) != 0) { if ((status = json_api->function(json, session, &json_reply)) != SWITCH_STATUS_SUCCESS) { cJSON_AddItemToObject(json, "status", cJSON_CreateString("error")); cJSON_AddItemToObject(json, "message", cJSON_CreateString("The command returned an error")); } else { cJSON_AddItemToObject(json, "status", cJSON_CreateString("success")); } if (!json_reply) { json_reply = cJSON_CreateNull(); } if (retval) { *retval = json_reply; } else { cJSON_AddItemToObject(json, "response", json_reply); } UNPROTECT_INTERFACE(json_api); } else { status = SWITCH_STATUS_FALSE; cJSON_AddItemToObject(json, "status", cJSON_CreateString("error")); cJSON_AddItemToObject(json, "message", cJSON_CreateString("Invalid request or non-existant command")); cJSON_AddItemToObject(json, "response", cJSON_CreateNull()); } return status; } SWITCH_DECLARE(switch_loadable_module_interface_t *) switch_loadable_module_create_module_interface(switch_memory_pool_t *pool, const char *name) { switch_loadable_module_interface_t *mod; mod = switch_core_alloc(pool, sizeof(switch_loadable_module_interface_t)); switch_assert(mod != NULL); mod->pool = pool; mod->module_name = switch_core_strdup(mod->pool, name); switch_thread_rwlock_create(&mod->rwlock, mod->pool); return mod; } #define ALLOC_INTERFACE(_TYPE_) { \ switch_##_TYPE_##_interface_t *i, *ptr; \ i = switch_core_alloc(mod->pool, sizeof(switch_##_TYPE_##_interface_t)); \ switch_assert(i != NULL); \ for (ptr = mod->_TYPE_##_interface; ptr && ptr->next; ptr = ptr->next); \ if (ptr) { \ ptr->next = i; \ } else { \ mod->_TYPE_##_interface = i; \ } \ switch_thread_rwlock_create(&i->rwlock, mod->pool); \ switch_mutex_init(&i->reflock, SWITCH_MUTEX_NESTED, mod->pool); \ i->parent = mod; \ return i; } SWITCH_DECLARE(void *) switch_loadable_module_create_interface(switch_loadable_module_interface_t *mod, switch_module_interface_name_t iname) { switch (iname) { case SWITCH_ENDPOINT_INTERFACE: ALLOC_INTERFACE(endpoint) case SWITCH_TIMER_INTERFACE: ALLOC_INTERFACE(timer) case SWITCH_DIALPLAN_INTERFACE: ALLOC_INTERFACE(dialplan) case SWITCH_CODEC_INTERFACE: ALLOC_INTERFACE(codec) case SWITCH_APPLICATION_INTERFACE: ALLOC_INTERFACE(application) case SWITCH_CHAT_APPLICATION_INTERFACE: ALLOC_INTERFACE(chat_application) case SWITCH_API_INTERFACE: ALLOC_INTERFACE(api) case SWITCH_JSON_API_INTERFACE: ALLOC_INTERFACE(json_api) case SWITCH_FILE_INTERFACE: ALLOC_INTERFACE(file) case SWITCH_SPEECH_INTERFACE: ALLOC_INTERFACE(speech) case SWITCH_DIRECTORY_INTERFACE: ALLOC_INTERFACE(directory) case SWITCH_CHAT_INTERFACE: ALLOC_INTERFACE(chat) case SWITCH_SAY_INTERFACE: ALLOC_INTERFACE(say) case SWITCH_ASR_INTERFACE: ALLOC_INTERFACE(asr) case SWITCH_MANAGEMENT_INTERFACE: ALLOC_INTERFACE(management) case SWITCH_LIMIT_INTERFACE: ALLOC_INTERFACE(limit) default: switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid Module Type!\n"); return NULL; } } struct switch_say_file_handle { char *ext; int cnt; struct switch_stream_handle stream; switch_event_t *param_event; }; SWITCH_DECLARE(char *) switch_say_file_handle_get_variable(switch_say_file_handle_t *sh, const char *var) { char *ret = NULL; if (sh->param_event) { ret = switch_event_get_header(sh->param_event, var); } return ret; } SWITCH_DECLARE(char *) switch_say_file_handle_get_path(switch_say_file_handle_t *sh) { return (char *) sh->stream.data; } SWITCH_DECLARE(char *) switch_say_file_handle_detach_path(switch_say_file_handle_t *sh) { char *path; switch_assert(sh); path = (char *) sh->stream.data; sh->stream.data = NULL; return path; } SWITCH_DECLARE(void) switch_say_file_handle_destroy(switch_say_file_handle_t **sh) { switch_assert(sh); switch_safe_free((*sh)->stream.data); switch_safe_free((*sh)->ext); if ((*sh)->param_event) { switch_event_destroy(&(*sh)->param_event); } free(*sh); *sh = NULL; } SWITCH_DECLARE(switch_status_t) switch_say_file_handle_create(switch_say_file_handle_t **sh, const char *ext, switch_event_t **var_event) { switch_assert(sh); if (zstr(ext)) { ext = "wav"; } *sh = malloc(sizeof(**sh)); memset(*sh, 0, sizeof(**sh)); SWITCH_STANDARD_STREAM((*sh)->stream); if (var_event) { (*sh)->param_event = *var_event; *var_event = NULL; } (*sh)->ext = strdup(ext); return SWITCH_STATUS_SUCCESS; } SWITCH_DECLARE(void) switch_say_file(switch_say_file_handle_t *sh, const char *fmt, ...) { char buf[256] = ""; int ret; va_list ap; va_start(ap, fmt); if ((ret = switch_vsnprintf(buf, sizeof(buf), fmt, ap)) > 0) { if (!sh->cnt++) { sh->stream.write_function(&sh->stream, "file_string://%s.%s", buf, sh->ext); } else if (strstr(buf, "://")) { sh->stream.write_function(&sh->stream, "!%s", buf); } else { sh->stream.write_function(&sh->stream, "!%s.%s", buf, sh->ext); } } va_end(ap); } SWITCH_DECLARE(switch_core_recover_callback_t) switch_core_get_secondary_recover_callback(const char *key) { switch_core_recover_callback_t cb; switch_mutex_lock(loadable_modules.mutex); cb = (switch_core_recover_callback_t) (intptr_t) switch_core_hash_find(loadable_modules.secondary_recover_hash, key); switch_mutex_unlock(loadable_modules.mutex); return cb; } SWITCH_DECLARE(switch_status_t) switch_core_register_secondary_recover_callback(const char *key, switch_core_recover_callback_t cb) { switch_status_t status = SWITCH_STATUS_SUCCESS; switch_assert(cb); switch_mutex_lock(loadable_modules.mutex); if (switch_core_hash_find(loadable_modules.secondary_recover_hash, key)) { status = SWITCH_STATUS_FALSE; } else { switch_core_hash_insert(loadable_modules.secondary_recover_hash, key, (void *)(intptr_t) cb); } switch_mutex_unlock(loadable_modules.mutex); return status; } SWITCH_DECLARE(void) switch_core_unregister_secondary_recover_callback(const char *key) { switch_mutex_lock(loadable_modules.mutex); switch_core_hash_delete(loadable_modules.secondary_recover_hash, key); switch_mutex_unlock(loadable_modules.mutex); } /* For Emacs: * Local Variables: * mode:c * indent-tabs-mode:t * tab-width:4 * c-basic-offset:4 * End: * For VIM: * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet: */ [-- Attachment #5 --] /* * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application * Copyright (C) 2005-2014, Anthony Minessale II <anthm@freeswitch.org> * * Version: MPL 1.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application * * The Initial Developer of the Original Code is * Anthony Minessale II <anthm@freeswitch.org> * Portions created by the Initial Developer are Copyright (C) * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Anthony Minessale II <anthm@freeswitch.org> * Michael Jerris <mike@jerris.com> * Paul D. Tinsley <pdt at jackhammer.org> * Marcel Barbulescu <marcelbarbulescu@gmail.com> * Joseph Sullivan <jossulli@amazon.com> * Seven Du <dujinfang@gmail.com> * * switch_core.c -- Main Core Library * */ #include <switch.h> #include <switch_ssl.h> #include <switch_stun.h> #include <switch_nat.h> #include "private/switch_core_pvt.h" #include <switch_curl.h> #include <switch_msrp.h> #ifndef WIN32 #include <switch_private.h> #ifdef HAVE_SETRLIMIT #include <sys/resource.h> #endif #endif #include <errno.h> #include <sqlite3.h> #ifdef HAVE_SYS_PRCTL_H #include <sys/prctl.h> #endif #ifdef SOLARIS_PRIVILEGES #include <priv.h> #endif SWITCH_DECLARE_DATA switch_directories SWITCH_GLOBAL_dirs = { 0 }; SWITCH_DECLARE_DATA switch_filenames SWITCH_GLOBAL_filenames = { 0 }; /* The main runtime obj we keep this hidden for ourselves */ struct switch_runtime runtime = { 0 }; static void switch_load_core_config(const char *file); static void send_heartbeat(void) { switch_event_t *event; switch_core_time_duration_t duration; switch_core_measure_time(switch_core_uptime(), &duration); if (switch_event_create(&event, SWITCH_EVENT_HEARTBEAT) == SWITCH_STATUS_SUCCESS) { switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Event-Info", "System Ready"); switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Up-Time", "%u year%s, " "%u day%s, " "%u hour%s, " "%u minute%s, " "%u second%s, " "%u millisecond%s, " "%u microsecond%s", duration.yr, duration.yr == 1 ? "" : "s", duration.day, duration.day == 1 ? "" : "s", duration.hr, duration.hr == 1 ? "" : "s", duration.min, duration.min == 1 ? "" : "s", duration.sec, duration.sec == 1 ? "" : "s", duration.ms, duration.ms == 1 ? "" : "s", duration.mms, duration.mms == 1 ? "" : "s"); switch_event_add_header(event, SWITCH_STACK_BOTTOM, "FreeSWITCH-Version", "%s", switch_version_full()); switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Uptime-msec", "%"SWITCH_TIME_T_FMT, switch_core_uptime() / 1000); switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Session-Count", "%u", switch_core_session_count()); switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Max-Sessions", "%u", switch_core_session_limit(0)); switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Session-Per-Sec", "%u", runtime.sps); switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Session-Per-Sec-Last", "%u", runtime.sps_last); switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Session-Per-Sec-Max", "%u", runtime.sps_peak); switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Session-Per-Sec-FiveMin", "%u", runtime.sps_peak_fivemin); switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Session-Since-Startup", "%" SWITCH_SIZE_T_FMT, switch_core_session_id() - 1); switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Session-Peak-Max", "%u", runtime.sessions_peak); switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Session-Peak-FiveMin", "%u", runtime.sessions_peak_fivemin); switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Idle-CPU", "%f", switch_core_idle_cpu()); switch_event_fire(&event); } } static char main_ip4[256] = ""; static char main_ip6[256] = ""; static void check_ip(void) { char guess_ip4[256] = ""; char guess_ip6[256] = ""; char old_ip4[256] = ""; char old_ip6[256] = ""; int ok4 = 1, ok6 = 1; int mask = 0; switch_status_t check6, check4; switch_event_t *event; char *hostname = switch_core_get_variable("hostname"); gethostname(runtime.hostname, sizeof(runtime.hostname)); if (zstr(hostname)) { switch_core_set_variable("hostname", runtime.hostname); } else if (strcmp(hostname, runtime.hostname)) { if (switch_event_create(&event, SWITCH_EVENT_TRAP) == SWITCH_STATUS_SUCCESS) { switch_event_add_header(event, SWITCH_STACK_BOTTOM, "condition", "hostname-change"); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "old-hostname", hostname ? hostname : "nil"); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "new-hostname", runtime.hostname); switch_event_fire(&event); } switch_core_set_variable("hostname", runtime.hostname); } check4 = switch_find_local_ip(guess_ip4, sizeof(guess_ip4), &mask, AF_INET); check6 = switch_find_local_ip(guess_ip6, sizeof(guess_ip6), NULL, AF_INET6); if (check6 != SWITCH_STATUS_SUCCESS && (zstr(main_ip6) || !strcasecmp(main_ip6, "::1"))) { check6 = SWITCH_STATUS_SUCCESS; } if (check4 != SWITCH_STATUS_SUCCESS) { ok4 = 2; } else if (!*main_ip4) { switch_set_string(main_ip4, guess_ip4); } else { if (!(ok4 = !strcmp(main_ip4, guess_ip4))) { struct in_addr in; in.s_addr = mask; switch_set_string(old_ip4, main_ip4); switch_set_string(main_ip4, guess_ip4); switch_core_set_variable("local_ip_v4", guess_ip4); switch_core_set_variable("local_mask_v4", inet_ntoa(in)); } } if (check6 != SWITCH_STATUS_SUCCESS) { ok6 = 2; } else if (!*main_ip6) { switch_set_string(main_ip6, guess_ip6); } else { if (!(ok6 = !strcmp(main_ip6, guess_ip6))) { switch_set_string(old_ip6, main_ip6); switch_set_string(main_ip6, guess_ip6); switch_core_set_variable("local_ip_v6", guess_ip6); } } if (!ok4 || !ok6) { if (switch_event_create(&event, SWITCH_EVENT_TRAP) == SWITCH_STATUS_SUCCESS) { switch_event_add_header(event, SWITCH_STACK_BOTTOM, "condition", "network-address-change"); if (!ok4) { switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "network-address-previous-v4", old_ip4); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "network-address-change-v4", main_ip4); } if (!ok6) { switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "network-address-previous-v6", old_ip6); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "network-address-change-v6", main_ip6); } switch_event_fire(&event); } } if (ok4 == 2 || ok6 == 2) { if (switch_event_create(&event, SWITCH_EVENT_TRAP) == SWITCH_STATUS_SUCCESS) { switch_event_add_header(event, SWITCH_STACK_BOTTOM, "condition", "network-outage"); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "network-status-v4", ok4 == 2 ? "disconnected" : "active"); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "network-address-v4", main_ip4); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "network-status-v6", ok6 == 2 ? "disconnected" : "active"); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "network-address-v6", main_ip6); switch_event_fire(&event); } } } SWITCH_STANDARD_SCHED_FUNC(heartbeat_callback) { send_heartbeat(); /* reschedule this task */ task->runtime = switch_epoch_time_now(NULL) + runtime.event_heartbeat_interval; } SWITCH_STANDARD_SCHED_FUNC(check_ip_callback) { check_ip(); /* reschedule this task */ task->runtime = switch_epoch_time_now(NULL) + 60; } SWITCH_DECLARE(switch_status_t) switch_core_set_console(const char *console) { if ((runtime.console = fopen(console, "a")) == 0) { fprintf(stderr, "Cannot open output file %s.\n", console); return SWITCH_STATUS_FALSE; } return SWITCH_STATUS_SUCCESS; } SWITCH_DECLARE(FILE *) switch_core_get_console(void) { return runtime.console; } #ifdef HAVE_SYS_IOCTL_H #include <sys/ioctl.h> #endif SWITCH_DECLARE(void) switch_core_screen_size(int *x, int *y) { #ifdef WIN32 CONSOLE_SCREEN_BUFFER_INFO csbi; int ret; if ((ret = GetConsoleScreenBufferInfo(GetStdHandle( STD_OUTPUT_HANDLE ), &csbi))) { if (x) *x = csbi.dwSize.X; if (y) *y = csbi.dwSize.Y; } #elif defined(TIOCGWINSZ) struct winsize w; ioctl(0, TIOCGWINSZ, &w); if (x) *x = w.ws_col; if (y) *y = w.ws_row; #else if (x) *x = 80; if (y) *y = 24; #endif } SWITCH_DECLARE(FILE *) switch_core_data_channel(switch_text_channel_t channel) { FILE *handle = stdout; switch (channel) { case SWITCH_CHANNEL_ID_LOG: case SWITCH_CHANNEL_ID_LOG_CLEAN: handle = runtime.console; break; default: handle = runtime.console; break; } return handle; } SWITCH_DECLARE(void) switch_core_remove_state_handler(const switch_state_handler_table_t *state_handler) { int index, tmp_index = 0; const switch_state_handler_table_t *tmp[SWITCH_MAX_STATE_HANDLERS + 1] = { 0 }; switch_mutex_lock(runtime.global_mutex); for (index = 0; index < runtime.state_handler_index; index++) { const switch_state_handler_table_t *cur = runtime.state_handlers[index]; runtime.state_handlers[index] = NULL; if (cur == state_handler) { continue; } tmp[tmp_index++] = cur; } runtime.state_handler_index = 0; for (index = 0; index < tmp_index; index++) { runtime.state_handlers[runtime.state_handler_index++] = tmp[index]; } switch_mutex_unlock(runtime.global_mutex); } SWITCH_DECLARE(int) switch_core_add_state_handler(const switch_state_handler_table_t *state_handler) { int index; switch_mutex_lock(runtime.global_mutex); index = runtime.state_handler_index; if (index > (SWITCH_MAX_STATE_HANDLERS - 1)) { index = -1; } else { runtime.state_handlers[index] = state_handler; runtime.state_handler_index++; } switch_mutex_unlock(runtime.global_mutex); return index; } SWITCH_DECLARE(const switch_state_handler_table_t *) switch_core_get_state_handler(int index) { if (index >= SWITCH_MAX_STATE_HANDLERS || index > runtime.state_handler_index) { return NULL; } return runtime.state_handlers[index]; } SWITCH_DECLARE(void) switch_core_dump_variables(switch_stream_handle_t *stream) { switch_event_header_t *hi; switch_mutex_lock(runtime.global_mutex); for (hi = runtime.global_vars->headers; hi; hi = hi->next) { stream->write_function(stream, "%s=%s\n", hi->name, hi->value); } switch_mutex_unlock(runtime.global_mutex); } SWITCH_DECLARE(const char *) switch_core_get_hostname(void) { return runtime.hostname; } SWITCH_DECLARE(const char *) switch_core_get_switchname(void) { if (!zstr(runtime.switchname)) return runtime.switchname; return runtime.hostname; } SWITCH_DECLARE(char *) switch_core_get_domain(switch_bool_t dup) { char *domain; const char *var; switch_thread_rwlock_rdlock(runtime.global_var_rwlock); if (!(var = switch_core_get_variable("domain"))) { var = "freeswitch.local"; } if (dup) { domain = strdup(var); } else { domain = (char *) var; } switch_thread_rwlock_unlock(runtime.global_var_rwlock); return domain; } SWITCH_DECLARE(switch_status_t) switch_core_get_variables(switch_event_t **event) { switch_status_t status; switch_thread_rwlock_rdlock(runtime.global_var_rwlock); status = switch_event_dup(event, runtime.global_vars); switch_thread_rwlock_unlock(runtime.global_var_rwlock); return status; } SWITCH_DECLARE(char *) switch_core_get_variable(const char *varname) { char *val; switch_thread_rwlock_rdlock(runtime.global_var_rwlock); val = (char *) switch_event_get_header(runtime.global_vars, varname); switch_thread_rwlock_unlock(runtime.global_var_rwlock); return val; } SWITCH_DECLARE(char *) switch_core_get_variable_dup(const char *varname) { char *val = NULL, *v; if (varname) { switch_thread_rwlock_rdlock(runtime.global_var_rwlock); if ((v = (char *) switch_event_get_header(runtime.global_vars, varname))) { val = strdup(v); } switch_thread_rwlock_unlock(runtime.global_var_rwlock); } return val; } SWITCH_DECLARE(char *) switch_core_get_variable_pdup(const char *varname, switch_memory_pool_t *pool) { char *val = NULL, *v; if (varname) { switch_thread_rwlock_rdlock(runtime.global_var_rwlock); if ((v = (char *) switch_event_get_header(runtime.global_vars, varname))) { val = switch_core_strdup(pool, v); } switch_thread_rwlock_unlock(runtime.global_var_rwlock); } return val; } static void switch_core_unset_variables(void) { switch_thread_rwlock_wrlock(runtime.global_var_rwlock); switch_event_destroy(&runtime.global_vars); switch_event_create_plain(&runtime.global_vars, SWITCH_EVENT_CHANNEL_DATA); switch_thread_rwlock_unlock(runtime.global_var_rwlock); } SWITCH_DECLARE(void) switch_core_set_variable(const char *varname, const char *value) { char *val; if (varname) { switch_thread_rwlock_wrlock(runtime.global_var_rwlock); val = (char *) switch_event_get_header(runtime.global_vars, varname); if (val) { switch_event_del_header(runtime.global_vars, varname); } if (value) { char *v = strdup(value); switch_string_var_check(v, SWITCH_TRUE); switch_event_add_header_string(runtime.global_vars, SWITCH_STACK_BOTTOM | SWITCH_STACK_NODUP, varname, v); } else { switch_event_del_header(runtime.global_vars, varname); } switch_thread_rwlock_unlock(runtime.global_var_rwlock); } } SWITCH_DECLARE(switch_bool_t) switch_core_set_var_conditional(const char *varname, const char *value, const char *val2) { char *val; if (varname) { switch_thread_rwlock_wrlock(runtime.global_var_rwlock); val = (char *) switch_event_get_header(runtime.global_vars, varname); if (val) { if (!val2 || strcmp(val, val2) != 0) { switch_thread_rwlock_unlock(runtime.global_var_rwlock); return SWITCH_FALSE; } switch_event_del_header(runtime.global_vars, varname); } else if (!zstr(val2)) { switch_thread_rwlock_unlock(runtime.global_var_rwlock); return SWITCH_FALSE; } if (value) { char *v = strdup(value); switch_string_var_check(v, SWITCH_TRUE); switch_event_add_header_string(runtime.global_vars, SWITCH_STACK_BOTTOM | SWITCH_STACK_NODUP, varname, v); } else { switch_event_del_header(runtime.global_vars, varname); } switch_thread_rwlock_unlock(runtime.global_var_rwlock); } return SWITCH_TRUE; } SWITCH_DECLARE(char *) switch_core_get_uuid(void) { return runtime.uuid_str; } static void *SWITCH_THREAD_FUNC switch_core_service_thread(switch_thread_t *thread, void *obj) { switch_core_session_t *session = obj; switch_channel_t *channel; switch_frame_t *read_frame = NULL; // switch_assert(thread != NULL); // switch_assert(session != NULL); if (switch_core_session_read_lock(session) != SWITCH_STATUS_SUCCESS) { return NULL; } switch_mutex_lock(session->frame_read_mutex); channel = switch_core_session_get_channel(session); switch_channel_set_flag(channel, CF_SERVICE); while (switch_channel_test_flag(channel, CF_SERVICE)) { if (switch_channel_test_flag(channel, CF_SERVICE_AUDIO)) { switch (switch_core_session_read_frame(session, &read_frame, SWITCH_IO_FLAG_NONE, 0)) { case SWITCH_STATUS_SUCCESS: case SWITCH_STATUS_TIMEOUT: case SWITCH_STATUS_BREAK: break; default: switch_channel_clear_flag(channel, CF_SERVICE); break; } } if (switch_channel_test_flag(channel, CF_SERVICE_VIDEO) && switch_channel_test_flag(channel, CF_VIDEO)) { switch (switch_core_session_read_video_frame(session, &read_frame, SWITCH_IO_FLAG_NONE, 0)) { case SWITCH_STATUS_SUCCESS: case SWITCH_STATUS_TIMEOUT: case SWITCH_STATUS_BREAK: break; default: switch_channel_clear_flag(channel, CF_SERVICE); break; } } } switch_mutex_unlock(session->frame_read_mutex); switch_channel_clear_flag(channel, CF_SERVICE_AUDIO); switch_channel_clear_flag(channel, CF_SERVICE_VIDEO); switch_core_session_rwunlock(session); return NULL; } /* Either add a timeout here or make damn sure the thread cannot get hung somehow (my preference) */ SWITCH_DECLARE(void) switch_core_thread_session_end(switch_core_session_t *session) { switch_channel_t *channel; switch_assert(session); channel = switch_core_session_get_channel(session); switch_assert(channel); switch_channel_clear_flag(channel, CF_SERVICE); switch_channel_clear_flag(channel, CF_SERVICE_AUDIO); switch_channel_clear_flag(channel, CF_SERVICE_VIDEO); switch_core_session_kill_channel(session, SWITCH_SIG_BREAK); } SWITCH_DECLARE(void) switch_core_service_session_av(switch_core_session_t *session, switch_bool_t audio, switch_bool_t video) { switch_channel_t *channel; switch_assert(session); channel = switch_core_session_get_channel(session); switch_assert(channel); if (audio) switch_channel_set_flag(channel, CF_SERVICE_AUDIO); if (video) switch_channel_set_flag(channel, CF_SERVICE_VIDEO); switch_core_session_launch_thread(session, (void *(*)(switch_thread_t *,void *))switch_core_service_thread, session); } /* This function abstracts the thread creation for modules by allowing you to pass a function ptr and a void object and trust that that the function will be run in a thread with arg This lets you request and activate a thread without giving up any knowledge about what is in the thread neither the core nor the calling module know anything about each other. This thread is expected to never exit until the application exits so the func is responsible to make sure that is the case. The typical use for this is so switch_loadable_module.c can start up a thread for each module passing the table of module methods as a session obj into the core without actually allowing the core to have any clue and keeping switch_loadable_module.c from needing any thread code. */ SWITCH_DECLARE(switch_thread_t *) switch_core_launch_thread(switch_thread_start_t func, void *obj, switch_memory_pool_t *pool) { switch_thread_t *thread = NULL; switch_threadattr_t *thd_attr = NULL; switch_core_thread_session_t *ts; int mypool; mypool = pool ? 0 : 1; if (!pool && switch_core_new_memory_pool(&pool) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Could not allocate memory pool\n"); return NULL; } switch_threadattr_create(&thd_attr, pool); if ((ts = switch_core_alloc(pool, sizeof(*ts))) == 0) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Could not allocate memory\n"); } else { if (mypool) { ts->pool = pool; } ts->objs[0] = obj; ts->objs[1] = thread; switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE); switch_threadattr_priority_set(thd_attr, SWITCH_PRI_REALTIME); switch_thread_create(&thread, thd_attr, func, ts, pool); } return thread; } SWITCH_DECLARE(void) switch_core_set_globals(void) { #define BUFSIZE 1024 #ifdef WIN32 char lpPathBuffer[BUFSIZE]; DWORD dwBufSize = BUFSIZE; char base_dir[1024]; char *lastbacklash; char *tmp; GetModuleFileName(NULL, base_dir, BUFSIZE); lastbacklash = strrchr(base_dir, '\\'); base_dir[(lastbacklash - base_dir)] = '\0'; /* set base_dir as cwd, to be able to use relative paths in scripting languages (e.g. mod_lua) when FS is running as a service or while debugging FS using visual studio */ SetCurrentDirectory(base_dir); tmp = switch_string_replace(base_dir, "\\", "/"); strcpy(base_dir, tmp); free(tmp); #else char base_dir[1024] = SWITCH_PREFIX_DIR; #endif /* Order of precedence for, eg, rundir: * -run * -base * --with-rundir * --prefix */ if (!SWITCH_GLOBAL_dirs.mod_dir && (SWITCH_GLOBAL_dirs.mod_dir = (char *) malloc(BUFSIZE))) { if (SWITCH_GLOBAL_dirs.base_dir) switch_snprintf(SWITCH_GLOBAL_dirs.mod_dir, BUFSIZE, "%s%smod", SWITCH_GLOBAL_dirs.base_dir, SWITCH_PATH_SEPARATOR); else #ifdef SWITCH_MOD_DIR switch_snprintf(SWITCH_GLOBAL_dirs.mod_dir, BUFSIZE, "%s", SWITCH_MOD_DIR); #else switch_snprintf(SWITCH_GLOBAL_dirs.mod_dir, BUFSIZE, "%s%smod", base_dir, SWITCH_PATH_SEPARATOR); #endif } if (!SWITCH_GLOBAL_dirs.lib_dir && (SWITCH_GLOBAL_dirs.lib_dir = (char *) malloc(BUFSIZE))) { if (SWITCH_GLOBAL_dirs.base_dir) switch_snprintf(SWITCH_GLOBAL_dirs.lib_dir, BUFSIZE, "%s%slib", SWITCH_GLOBAL_dirs.base_dir, SWITCH_PATH_SEPARATOR); else #ifdef SWITCH_LIB_DIR switch_snprintf(SWITCH_GLOBAL_dirs.lib_dir, BUFSIZE, "%s", SWITCH_LIB_DIR); #else switch_snprintf(SWITCH_GLOBAL_dirs.lib_dir, BUFSIZE, "%s%slib", base_dir, SWITCH_PATH_SEPARATOR); #endif } if (!SWITCH_GLOBAL_dirs.conf_dir && (SWITCH_GLOBAL_dirs.conf_dir = (char *) malloc(BUFSIZE))) { if (SWITCH_GLOBAL_dirs.base_dir) switch_snprintf(SWITCH_GLOBAL_dirs.conf_dir, BUFSIZE, "%s%sconf", SWITCH_GLOBAL_dirs.base_dir, SWITCH_PATH_SEPARATOR); else #ifdef SWITCH_CONF_DIR switch_snprintf(SWITCH_GLOBAL_dirs.conf_dir, BUFSIZE, "%s", SWITCH_CONF_DIR); #else switch_snprintf(SWITCH_GLOBAL_dirs.conf_dir, BUFSIZE, "%s%sconf", base_dir, SWITCH_PATH_SEPARATOR); #endif } if (!SWITCH_GLOBAL_dirs.log_dir && (SWITCH_GLOBAL_dirs.log_dir = (char *) malloc(BUFSIZE))) { if (SWITCH_GLOBAL_dirs.base_dir) switch_snprintf(SWITCH_GLOBAL_dirs.log_dir, BUFSIZE, "%s%slog", SWITCH_GLOBAL_dirs.base_dir, SWITCH_PATH_SEPARATOR); else #ifdef SWITCH_LOG_DIR switch_snprintf(SWITCH_GLOBAL_dirs.log_dir, BUFSIZE, "%s", SWITCH_LOG_DIR); #else switch_snprintf(SWITCH_GLOBAL_dirs.log_dir, BUFSIZE, "%s%slog", base_dir, SWITCH_PATH_SEPARATOR); #endif } if (!SWITCH_GLOBAL_dirs.run_dir && (SWITCH_GLOBAL_dirs.run_dir = (char *) malloc(BUFSIZE))) { if (SWITCH_GLOBAL_dirs.base_dir) switch_snprintf(SWITCH_GLOBAL_dirs.run_dir, BUFSIZE, "%s%srun", SWITCH_GLOBAL_dirs.base_dir, SWITCH_PATH_SEPARATOR); else #ifdef SWITCH_RUN_DIR switch_snprintf(SWITCH_GLOBAL_dirs.run_dir, BUFSIZE, "%s", SWITCH_RUN_DIR); #else switch_snprintf(SWITCH_GLOBAL_dirs.run_dir, BUFSIZE, "%s%srun", base_dir, SWITCH_PATH_SEPARATOR); #endif } if (!SWITCH_GLOBAL_dirs.recordings_dir && (SWITCH_GLOBAL_dirs.recordings_dir = (char *) malloc(BUFSIZE))) { if (SWITCH_GLOBAL_dirs.base_dir) switch_snprintf(SWITCH_GLOBAL_dirs.recordings_dir, BUFSIZE, "%s%srecordings", SWITCH_GLOBAL_dirs.base_dir, SWITCH_PATH_SEPARATOR); else #ifdef SWITCH_RECORDINGS_DIR switch_snprintf(SWITCH_GLOBAL_dirs.recordings_dir, BUFSIZE, "%s", SWITCH_RECORDINGS_DIR); #else switch_snprintf(SWITCH_GLOBAL_dirs.recordings_dir, BUFSIZE, "%s%srecordings", base_dir, SWITCH_PATH_SEPARATOR); #endif } if (!SWITCH_GLOBAL_dirs.sounds_dir && (SWITCH_GLOBAL_dirs.sounds_dir = (char *) malloc(BUFSIZE))) { if (SWITCH_GLOBAL_dirs.base_dir) switch_snprintf(SWITCH_GLOBAL_dirs.sounds_dir, BUFSIZE, "%s%ssounds", SWITCH_GLOBAL_dirs.base_dir, SWITCH_PATH_SEPARATOR); else #ifdef SWITCH_SOUNDS_DIR switch_snprintf(SWITCH_GLOBAL_dirs.sounds_dir, BUFSIZE, "%s", SWITCH_SOUNDS_DIR); #else switch_snprintf(SWITCH_GLOBAL_dirs.sounds_dir, BUFSIZE, "%s%ssounds", base_dir, SWITCH_PATH_SEPARATOR); #endif } if (!SWITCH_GLOBAL_dirs.storage_dir && (SWITCH_GLOBAL_dirs.storage_dir = (char *) malloc(BUFSIZE))) { if (SWITCH_GLOBAL_dirs.base_dir) switch_snprintf(SWITCH_GLOBAL_dirs.storage_dir, BUFSIZE, "%s%sstorage", SWITCH_GLOBAL_dirs.base_dir, SWITCH_PATH_SEPARATOR); else #ifdef SWITCH_STORAGE_DIR switch_snprintf(SWITCH_GLOBAL_dirs.storage_dir, BUFSIZE, "%s", SWITCH_STORAGE_DIR); #else switch_snprintf(SWITCH_GLOBAL_dirs.storage_dir, BUFSIZE, "%s%sstorage", base_dir, SWITCH_PATH_SEPARATOR); #endif } if (!SWITCH_GLOBAL_dirs.cache_dir && (SWITCH_GLOBAL_dirs.cache_dir = (char *) malloc(BUFSIZE))) { if (SWITCH_GLOBAL_dirs.base_dir) switch_snprintf(SWITCH_GLOBAL_dirs.cache_dir, BUFSIZE, "%s%scache", SWITCH_GLOBAL_dirs.base_dir, SWITCH_PATH_SEPARATOR); else #ifdef SWITCH_CACHE_DIR switch_snprintf(SWITCH_GLOBAL_dirs.cache_dir, BUFSIZE, "%s", SWITCH_CACHE_DIR); #else switch_snprintf(SWITCH_GLOBAL_dirs.cache_dir, BUFSIZE, "%s%scache", base_dir, SWITCH_PATH_SEPARATOR); #endif } if (!SWITCH_GLOBAL_dirs.db_dir && (SWITCH_GLOBAL_dirs.db_dir = (char *) malloc(BUFSIZE))) { if (SWITCH_GLOBAL_dirs.base_dir) switch_snprintf(SWITCH_GLOBAL_dirs.db_dir, BUFSIZE, "%s%sdb", SWITCH_GLOBAL_dirs.base_dir, SWITCH_PATH_SEPARATOR); else #ifdef SWITCH_DB_DIR switch_snprintf(SWITCH_GLOBAL_dirs.db_dir, BUFSIZE, "%s", SWITCH_DB_DIR); #else switch_snprintf(SWITCH_GLOBAL_dirs.db_dir, BUFSIZE, "%s%sdb", base_dir, SWITCH_PATH_SEPARATOR); #endif } if (!SWITCH_GLOBAL_dirs.script_dir && (SWITCH_GLOBAL_dirs.script_dir = (char *) malloc(BUFSIZE))) { if (SWITCH_GLOBAL_dirs.base_dir) switch_snprintf(SWITCH_GLOBAL_dirs.script_dir, BUFSIZE, "%s%sscripts", SWITCH_GLOBAL_dirs.base_dir, SWITCH_PATH_SEPARATOR); else #ifdef SWITCH_SCRIPT_DIR switch_snprintf(SWITCH_GLOBAL_dirs.script_dir, BUFSIZE, "%s", SWITCH_SCRIPT_DIR); #else switch_snprintf(SWITCH_GLOBAL_dirs.script_dir, BUFSIZE, "%s%sscripts", base_dir, SWITCH_PATH_SEPARATOR); #endif } if (!SWITCH_GLOBAL_dirs.htdocs_dir && (SWITCH_GLOBAL_dirs.htdocs_dir = (char *) malloc(BUFSIZE))) { if (SWITCH_GLOBAL_dirs.base_dir) switch_snprintf(SWITCH_GLOBAL_dirs.htdocs_dir, BUFSIZE, "%s%shtdocs", SWITCH_GLOBAL_dirs.base_dir, SWITCH_PATH_SEPARATOR); else #ifdef SWITCH_HTDOCS_DIR switch_snprintf(SWITCH_GLOBAL_dirs.htdocs_dir, BUFSIZE, "%s", SWITCH_HTDOCS_DIR); #else switch_snprintf(SWITCH_GLOBAL_dirs.htdocs_dir, BUFSIZE, "%s%shtdocs", base_dir, SWITCH_PATH_SEPARATOR); #endif } if (!SWITCH_GLOBAL_dirs.grammar_dir && (SWITCH_GLOBAL_dirs.grammar_dir = (char *) malloc(BUFSIZE))) { if (SWITCH_GLOBAL_dirs.base_dir) switch_snprintf(SWITCH_GLOBAL_dirs.grammar_dir, BUFSIZE, "%s%sgrammar", SWITCH_GLOBAL_dirs.base_dir, SWITCH_PATH_SEPARATOR); else #ifdef SWITCH_GRAMMAR_DIR switch_snprintf(SWITCH_GLOBAL_dirs.grammar_dir, BUFSIZE, "%s", SWITCH_GRAMMAR_DIR); #else switch_snprintf(SWITCH_GLOBAL_dirs.grammar_dir, BUFSIZE, "%s%sgrammar", base_dir, SWITCH_PATH_SEPARATOR); #endif } if (!SWITCH_GLOBAL_dirs.fonts_dir && (SWITCH_GLOBAL_dirs.fonts_dir = (char *) malloc(BUFSIZE))) { if (SWITCH_GLOBAL_dirs.base_dir) switch_snprintf(SWITCH_GLOBAL_dirs.fonts_dir, BUFSIZE, "%s%sfonts", SWITCH_GLOBAL_dirs.base_dir, SWITCH_PATH_SEPARATOR); else #ifdef SWITCH_FONTS_DIR switch_snprintf(SWITCH_GLOBAL_dirs.fonts_dir, BUFSIZE, "%s", SWITCH_FONTS_DIR); #else switch_snprintf(SWITCH_GLOBAL_dirs.fonts_dir, BUFSIZE, "%s%sfonts", base_dir, SWITCH_PATH_SEPARATOR); #endif } if (!SWITCH_GLOBAL_dirs.images_dir && (SWITCH_GLOBAL_dirs.images_dir = (char *) malloc(BUFSIZE))) { if (SWITCH_GLOBAL_dirs.base_dir) switch_snprintf(SWITCH_GLOBAL_dirs.images_dir, BUFSIZE, "%s%simages", SWITCH_GLOBAL_dirs.base_dir, SWITCH_PATH_SEPARATOR); else #ifdef SWITCH_IMAGES_DIR switch_snprintf(SWITCH_GLOBAL_dirs.images_dir, BUFSIZE, "%s", SWITCH_IMAGES_DIR); #else switch_snprintf(SWITCH_GLOBAL_dirs.images_dir, BUFSIZE, "%s%simages", base_dir, SWITCH_PATH_SEPARATOR); #endif } if (!SWITCH_GLOBAL_dirs.data_dir && (SWITCH_GLOBAL_dirs.data_dir = (char *) malloc(BUFSIZE))) { if (SWITCH_GLOBAL_dirs.base_dir) switch_snprintf(SWITCH_GLOBAL_dirs.data_dir, BUFSIZE, "%s", SWITCH_GLOBAL_dirs.base_dir); else #ifdef SWITCH_DATA_DIR switch_snprintf(SWITCH_GLOBAL_dirs.data_dir, BUFSIZE, "%s", SWITCH_DATA_DIR); #else switch_snprintf(SWITCH_GLOBAL_dirs.data_dir, BUFSIZE, "%s", base_dir); #endif } if (!SWITCH_GLOBAL_dirs.localstate_dir && (SWITCH_GLOBAL_dirs.localstate_dir = (char *) malloc(BUFSIZE))) { if (SWITCH_GLOBAL_dirs.base_dir) switch_snprintf(SWITCH_GLOBAL_dirs.localstate_dir, BUFSIZE, "%s", SWITCH_GLOBAL_dirs.base_dir); else #ifdef SWITCH_LOCALSTATE_DIR switch_snprintf(SWITCH_GLOBAL_dirs.localstate_dir, BUFSIZE, "%s", SWITCH_LOCALSTATE_DIR); #else switch_snprintf(SWITCH_GLOBAL_dirs.localstate_dir, BUFSIZE, "%s", base_dir); #endif } if (!SWITCH_GLOBAL_dirs.certs_dir && (SWITCH_GLOBAL_dirs.certs_dir = (char *) malloc(BUFSIZE))) { if (SWITCH_GLOBAL_dirs.base_dir) switch_snprintf(SWITCH_GLOBAL_dirs.certs_dir, BUFSIZE, "%s%scert", SWITCH_GLOBAL_dirs.base_dir, SWITCH_PATH_SEPARATOR); else #ifdef SWITCH_CERTS_DIR switch_snprintf(SWITCH_GLOBAL_dirs.certs_dir, BUFSIZE, "%s", SWITCH_CERTS_DIR); #else switch_snprintf(SWITCH_GLOBAL_dirs.certs_dir, BUFSIZE, "%s%scert", base_dir, SWITCH_PATH_SEPARATOR); #endif } if (!SWITCH_GLOBAL_dirs.temp_dir && (SWITCH_GLOBAL_dirs.temp_dir = (char *) malloc(BUFSIZE))) { #ifdef SWITCH_TEMP_DIR switch_snprintf(SWITCH_GLOBAL_dirs.temp_dir, BUFSIZE, "%s", SWITCH_TEMP_DIR); #else #ifdef WIN32 GetTempPath(dwBufSize, lpPathBuffer); lpPathBuffer[strlen(lpPathBuffer)-1] = 0; tmp = switch_string_replace(lpPathBuffer, "\\", "/"); strcpy(lpPathBuffer, tmp); free(tmp); switch_snprintf(SWITCH_GLOBAL_dirs.temp_dir, BUFSIZE, "%s", lpPathBuffer); #else switch_snprintf(SWITCH_GLOBAL_dirs.temp_dir, BUFSIZE, "%s", "/tmp"); #endif #endif } if (!SWITCH_GLOBAL_filenames.conf_name && (SWITCH_GLOBAL_filenames.conf_name = (char *) malloc(BUFSIZE))) { switch_snprintf(SWITCH_GLOBAL_filenames.conf_name, BUFSIZE, "%s", "freeswitch.xml"); } /* Do this last because it being empty is part of the above logic */ if (!SWITCH_GLOBAL_dirs.base_dir && (SWITCH_GLOBAL_dirs.base_dir = (char *) malloc(BUFSIZE))) { switch_snprintf(SWITCH_GLOBAL_dirs.base_dir, BUFSIZE, "%s", base_dir); } switch_assert(SWITCH_GLOBAL_dirs.base_dir); switch_assert(SWITCH_GLOBAL_dirs.mod_dir); switch_assert(SWITCH_GLOBAL_dirs.lib_dir); switch_assert(SWITCH_GLOBAL_dirs.conf_dir); switch_assert(SWITCH_GLOBAL_dirs.log_dir); switch_assert(SWITCH_GLOBAL_dirs.run_dir); switch_assert(SWITCH_GLOBAL_dirs.db_dir); switch_assert(SWITCH_GLOBAL_dirs.script_dir); switch_assert(SWITCH_GLOBAL_dirs.htdocs_dir); switch_assert(SWITCH_GLOBAL_dirs.grammar_dir); switch_assert(SWITCH_GLOBAL_dirs.fonts_dir); switch_assert(SWITCH_GLOBAL_dirs.images_dir); switch_assert(SWITCH_GLOBAL_dirs.recordings_dir); switch_assert(SWITCH_GLOBAL_dirs.sounds_dir); switch_assert(SWITCH_GLOBAL_dirs.certs_dir); switch_assert(SWITCH_GLOBAL_dirs.temp_dir); switch_assert(SWITCH_GLOBAL_dirs.data_dir); switch_assert(SWITCH_GLOBAL_dirs.localstate_dir); switch_assert(SWITCH_GLOBAL_filenames.conf_name); } SWITCH_DECLARE(int32_t) switch_core_set_process_privileges(void) { #ifdef SOLARIS_PRIVILEGES priv_set_t *basicset; /* make the process privilege-aware */ setpflags(PRIV_AWARE, 1); /* reset the privileges to basic */ basicset = priv_str_to_set("basic", ",", NULL); if (setppriv(PRIV_SET, PRIV_EFFECTIVE, basicset) != 0) { fprintf(stderr, "ERROR: Failed to acquire basic privileges (%s)\n", strerror(errno)); } /* we need high-resolution clock, and this requires a non-basic privilege */ if (priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_PROC_CLOCK_HIGHRES, NULL) < 0) { fprintf(stderr, "ERROR: Failed to acquire proc_clock_highres privilege (%s)\n", strerror(errno)); return -1; } /* need this for setrlimit */ if (priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_SYS_RESOURCE, NULL) < 0) { fprintf(stderr, "ERROR: Failed to acquire sys_resource privilege (%s)\n", strerror(errno)); return -1; } /* we need to read directories belonging to other uid */ if (priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_FILE_DAC_SEARCH, NULL) < 0) { fprintf(stderr, "ERROR: Failed to acquire file_dac_search privilege (%s)\n", strerror(errno)); return -1; } #endif return 0; } SWITCH_DECLARE(int32_t) set_low_priority(void) { #ifdef WIN32 return SetPriorityClass(GetCurrentProcess(), BELOW_NORMAL_PRIORITY_CLASS); #else #if defined(USE_SCHED_SETSCHEDULER) && ! defined(SOLARIS_PRIVILEGES) /* * Try to use a normal scheduler */ struct sched_param sched = { 0 }; sched.sched_priority = 0; if (sched_setscheduler(0, SCHED_OTHER, &sched) < 0) { fprintf(stderr, "ERROR: Failed to set SCHED_OTHER scheduler (%s)\n", strerror(errno)); return -1; } #endif #ifdef HAVE_SETPRIORITY /* * setpriority() works on FreeBSD (6.2), nice() doesn't */ if (setpriority(PRIO_PROCESS, getpid(), 19) < 0) { fprintf(stderr, "ERROR: Could not set nice level\n"); return -1; } #else if (nice(19) != 19) { fprintf(stderr, "ERROR: Could not set nice level\n"); return -1; } #endif return 0; #endif } SWITCH_DECLARE(int32_t) set_realtime_priority(void) { #ifdef WIN32 return SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS); #else #ifdef USE_SCHED_SETSCHEDULER /* * Try to use a round-robin scheduler * with a fallback if that does not work */ struct sched_param sched = { 0 }; sched.sched_priority = SWITCH_PRI_LOW; #endif #ifdef SOLARIS_PRIVILEGES /* request the privileges to elevate the priority */ if (priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_PROC_PRIOCNTL, NULL) < 0) { #ifdef PRIV_PROC_PRIOUP /* fallback to PRIV_PROC_PRIOUP on SmartOS */ fprintf(stderr, "WARN: Failed to acquire proc_priocntl privilege (%s)\n", strerror(errno)); if (priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_PROC_PRIOUP, NULL) < 0) { fprintf(stderr, "ERROR: Failed to acquire proc_prioup privilege (%s)\n", strerror(errno)); return -1; } #else fprintf(stderr, "ERROR: Failed to acquire proc_priocntl privilege (%s)\n", strerror(errno)); return -1; #endif } if (sched_setscheduler(0, SCHED_FIFO, &sched) < 0) { fprintf(stderr, "WARN: Failed to set SCHED_FIFO scheduler (%s)\n", strerror(errno)); } else { return 0; } if (setpriority(PRIO_PROCESS, 0, -10) < 0) { fprintf(stderr, "ERROR: Could not set nice level\n"); return -1; } return 0; #else #ifdef USE_SCHED_SETSCHEDULER if (sched_setscheduler(0, SCHED_FIFO, &sched) < 0) { fprintf(stderr, "ERROR: Failed to set SCHED_FIFO scheduler (%s)\n", strerror(errno)); sched.sched_priority = 0; if (sched_setscheduler(0, SCHED_OTHER, &sched) < 0 ) { fprintf(stderr, "ERROR: Failed to set SCHED_OTHER scheduler (%s)\n", strerror(errno)); return -1; } } #endif #ifdef HAVE_SETPRIORITY /* * setpriority() works on FreeBSD (6.2), nice() doesn't */ if (setpriority(PRIO_PROCESS, getpid(), -10) < 0) { fprintf(stderr, "ERROR: Could not set nice level\n"); return -1; } #else if (nice(-10) != -10) { fprintf(stderr, "ERROR: Could not set nice level\n"); return -1; } #endif #endif return 0; #endif } SWITCH_DECLARE(uint32_t) switch_core_cpu_count(void) { return runtime.cpu_count; } SWITCH_DECLARE(int32_t) set_normal_priority(void) { return 0; } SWITCH_DECLARE(int32_t) set_auto_priority(void) { #ifndef WIN32 runtime.cpu_count = sysconf (_SC_NPROCESSORS_ONLN); #else SYSTEM_INFO sysinfo; GetSystemInfo( &sysinfo ); runtime.cpu_count = sysinfo.dwNumberOfProcessors; #endif if (!runtime.cpu_count) runtime.cpu_count = 1; return set_realtime_priority(); // ERROR: code not reachable on Windows Visual Studio Express 2008 return 0; } SWITCH_DECLARE(int32_t) change_user_group(const char *user, const char *group) { #ifndef WIN32 uid_t runas_uid = 0; gid_t runas_gid = 0; struct passwd *runas_pw = NULL; if (user) { /* * Lookup user information in the system's db */ runas_pw = getpwnam(user); if (!runas_pw) { fprintf(stderr, "ERROR: Unknown user \"%s\"\n", user); return -1; } runas_uid = runas_pw->pw_uid; } if (group) { struct group *gr = NULL; /* * Lookup group information in the system's db */ gr = getgrnam(group); if (!gr) { fprintf(stderr, "ERROR: Unknown group \"%s\"\n", group); return -1; } runas_gid = gr->gr_gid; } if (runas_uid && getuid() == runas_uid && (!runas_gid || runas_gid == getgid())) { /* already running as the right user and group, nothing to do! */ return 0; } if (runas_uid) { #ifdef SOLARIS_PRIVILEGES /* request the privilege to set the UID */ if (priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_PROC_SETID, NULL) < 0) { fprintf(stderr, "ERROR: Failed to acquire proc_setid privilege (%s)\n", strerror(errno)); return -1; } #endif #ifdef HAVE_SETGROUPS /* * Drop all group memberships prior to changing anything * or else we're going to inherit the parent's list of groups * (which is not what we want...) */ if (setgroups(0, NULL) < 0) { fprintf(stderr, "ERROR: Failed to drop group access list\n"); return -1; } #endif if (runas_gid) { /* * A group has been passed, switch to it * (without loading the user's other groups) */ if (setgid(runas_gid) < 0) { fprintf(stderr, "ERROR: Failed to change gid!\n"); return -1; } } else { /* * No group has been passed, use the user's primary group in this case */ if (setgid(runas_pw->pw_gid) < 0) { fprintf(stderr, "ERROR: Failed to change gid!\n"); return -1; } #ifdef HAVE_INITGROUPS /* * Set all the other groups the user is a member of * (This can be really useful for fine-grained access control) */ if (initgroups(runas_pw->pw_name, runas_pw->pw_gid) < 0) { fprintf(stderr, "ERROR: Failed to set group access list for user\n"); return -1; } #endif } /* * Finally drop all privileges by switching to the new userid */ if (setuid(runas_uid) < 0) { fprintf(stderr, "ERROR: Failed to change uid!\n"); return -1; } #ifdef HAVE_SYS_PRCTL_H if (prctl(PR_SET_DUMPABLE, 1) < 0) { fprintf(stderr, "ERROR: Failed to enable core dumps!\n"); return -1; } #endif } #endif return 0; } SWITCH_DECLARE(void) switch_core_runtime_loop(int bg) { #ifdef WIN32 HANDLE shutdown_event; char path[256] = ""; #endif if (bg) { #ifdef WIN32 switch_snprintf(path, sizeof(path), "Global\\Freeswitch.%d", getpid()); shutdown_event = CreateEvent(NULL, FALSE, FALSE, path); if (shutdown_event) { WaitForSingleObject(shutdown_event, INFINITE); } #else while (runtime.running) { switch_yield(1000000); } #endif } else { /* wait for console input */ switch_console_loop(); } } SWITCH_DECLARE(const char *) switch_core_mime_ext2type(const char *ext) { if (!ext) { return NULL; } return (const char *) switch_core_hash_find(runtime.mime_types, ext); } SWITCH_DECLARE(const char *) switch_core_mime_type2ext(const char *mime) { if (!mime) { return NULL; } return (const char *) switch_core_hash_find(runtime.mime_type_exts, mime); } SWITCH_DECLARE(switch_hash_index_t *) switch_core_mime_index(void) { return switch_core_hash_first(runtime.mime_types); } SWITCH_DECLARE(switch_status_t) switch_core_mime_add_type(const char *type, const char *ext) { char *ptype = NULL; char *ext_list = NULL; int argc = 0; char *argv[20] = { 0 }; int x; switch_status_t status = SWITCH_STATUS_FALSE; switch_assert(type); switch_assert(ext); ptype = switch_core_permanent_strdup(type); ext_list = strdup(ext); switch_assert(ext_list); /* Map each file extension to this MIME type if not already mapped. Map the MIME type to the first file extension in the list if not already mapped. */ if ((argc = switch_separate_string(ext_list, ' ', argv, (sizeof(argv) / sizeof(argv[0]))))) { int is_mapped_type = switch_core_hash_find(runtime.mime_type_exts, ptype) != NULL; for (x = 0; x < argc; x++) { if (argv[x] && ptype) { if (!switch_core_hash_find(runtime.mime_types, ext)) { switch_core_hash_insert(runtime.mime_types, argv[x], ptype); } if (!is_mapped_type) { switch_core_hash_insert(runtime.mime_type_exts, ptype, switch_core_permanent_strdup(argv[x])); is_mapped_type = 1; } } } status = SWITCH_STATUS_SUCCESS; } free(ext_list); return status; } static void load_mime_types(void) { char *cf = "mime.types"; FILE *fd = NULL; char *line_buf = NULL; switch_size_t llen = 0; char *mime_path = NULL; mime_path = switch_mprintf("%s/%s", SWITCH_GLOBAL_dirs.conf_dir, cf); switch_assert(mime_path); fd = fopen(mime_path, "rb"); if (fd == NULL) { goto end; } while ((switch_fp_read_dline(fd, &line_buf, &llen))) { char *p; char *type = line_buf; if (*line_buf == '#') { continue; } if ((p = strchr(line_buf, '\r')) || (p = strchr(line_buf, '\n'))) { *p = '\0'; } if ((p = strchr(type, '\t')) || (p = strchr(type, ' '))) { *p++ = '\0'; while (*p == ' ' || *p == '\t') { p++; } switch_core_mime_add_type(type, p); } } switch_safe_free(line_buf); if (fd) { fclose(fd); fd = NULL; } end: switch_safe_free(mime_path); } SWITCH_DECLARE(void) switch_core_setrlimits(void) { #ifdef HAVE_SETRLIMIT struct rlimit rlp; /* Setting the stack size on FreeBSD results in an instant crash. If anyone knows how to fix this, feel free to submit a patch to https://freeswitch.org/jira */ #ifndef __FreeBSD__ memset(&rlp, 0, sizeof(rlp)); rlp.rlim_cur = SWITCH_THREAD_STACKSIZE; rlp.rlim_max = SWITCH_SYSTEM_THREAD_STACKSIZE; setrlimit(RLIMIT_STACK, &rlp); #endif memset(&rlp, 0, sizeof(rlp)); rlp.rlim_cur = 999999; rlp.rlim_max = 999999; setrlimit(RLIMIT_NOFILE, &rlp); memset(&rlp, 0, sizeof(rlp)); rlp.rlim_cur = RLIM_INFINITY; rlp.rlim_max = RLIM_INFINITY; setrlimit(RLIMIT_CPU, &rlp); setrlimit(RLIMIT_DATA, &rlp); setrlimit(RLIMIT_FSIZE, &rlp); #ifdef RLIMIT_NPROC setrlimit(RLIMIT_NPROC, &rlp); #endif #ifdef RLIMIT_RTPRIO setrlimit(RLIMIT_RTPRIO, &rlp); #endif #if !defined(__OpenBSD__) && !defined(__NetBSD__) setrlimit(RLIMIT_AS, &rlp); #endif #endif return; } typedef struct { switch_memory_pool_t *pool; switch_hash_t *hash; } switch_ip_list_t; static switch_ip_list_t IP_LIST = { 0 }; SWITCH_DECLARE(switch_bool_t) switch_check_network_list_ip_token(const char *ip_str, const char *list_name, const char **token) { switch_network_list_t *list; ip_t ip, mask, net; uint32_t bits; char *ipv6 = strchr(ip_str,':'); switch_bool_t ok = SWITCH_FALSE; char *ipv4 = NULL; if (!list_name) { return SWITCH_FALSE; } if ((ipv4 = switch_network_ipv4_mapped_ipv6_addr(ip_str))) { ip_str = ipv4; ipv6 = NULL; } switch_mutex_lock(runtime.global_mutex); if (ipv6) { switch_inet_pton(AF_INET6, ip_str, &ip); } else { switch_inet_pton(AF_INET, ip_str, &ip); ip.v4 = htonl(ip.v4); } if ((list = switch_core_hash_find(IP_LIST.hash, list_name))) { if (ipv6) { ok = switch_network_list_validate_ip6_token(list, ip, token); } else { ok = switch_network_list_validate_ip_token(list, ip.v4, token); } } else if (strchr(list_name, '/')) { if (strchr(list_name, ',')) { char *list_name_dup = strdup(list_name); char *argv[32]; int argc; switch_assert(list_name_dup); if ((argc = switch_separate_string(list_name_dup, ',', argv, (sizeof(argv) / sizeof(argv[0]))))) { int i; for (i = 0; i < argc; i++) { switch_parse_cidr(argv[i], &net, &mask, &bits); if (ipv6) { if ((ok = switch_testv6_subnet(ip, net, mask))){ break; } } else { if ((ok = switch_test_subnet(ip.v4, net.v4, mask.v4))) { break; } } } } free(list_name_dup); } else { switch_parse_cidr(list_name, &net, &mask, &bits); if (ipv6) { ok = switch_testv6_subnet(ip, net, mask); } else { ok = switch_test_subnet(ip.v4, net.v4, mask.v4); } } } switch_safe_free(ipv4); switch_mutex_unlock(runtime.global_mutex); return ok; } SWITCH_DECLARE(void) switch_load_network_lists(switch_bool_t reload) { switch_xml_t xml = NULL, x_lists = NULL, x_list = NULL, x_node = NULL, cfg = NULL; switch_network_list_t *rfc_list, *list; char guess_ip[16] = ""; int mask = 0; char guess_mask[16] = ""; char *tmp_name; struct in_addr in; switch_find_local_ip(guess_ip, sizeof(guess_ip), &mask, AF_INET); in.s_addr = mask; switch_set_string(guess_mask, inet_ntoa(in)); switch_mutex_lock(runtime.global_mutex); if (IP_LIST.hash) { switch_core_hash_destroy(&IP_LIST.hash); } if (IP_LIST.pool) { switch_core_destroy_memory_pool(&IP_LIST.pool); } memset(&IP_LIST, 0, sizeof(IP_LIST)); switch_core_new_memory_pool(&IP_LIST.pool); switch_core_hash_init(&IP_LIST.hash); tmp_name = "rfc6598.auto"; switch_network_list_create(&rfc_list, tmp_name, SWITCH_FALSE, IP_LIST.pool); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Created ip list %s default (deny)\n", tmp_name); switch_network_list_add_cidr(rfc_list, "100.64.0.0/10", SWITCH_TRUE); switch_core_hash_insert(IP_LIST.hash, tmp_name, rfc_list); tmp_name = "rfc1918.auto"; switch_network_list_create(&rfc_list, tmp_name, SWITCH_FALSE, IP_LIST.pool); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Created ip list %s default (deny)\n", tmp_name); switch_network_list_add_cidr(rfc_list, "10.0.0.0/8", SWITCH_TRUE); switch_network_list_add_cidr(rfc_list, "172.16.0.0/12", SWITCH_TRUE); switch_network_list_add_cidr(rfc_list, "192.168.0.0/16", SWITCH_TRUE); switch_network_list_add_cidr(rfc_list, "fe80::/10", SWITCH_TRUE); switch_core_hash_insert(IP_LIST.hash, tmp_name, rfc_list); tmp_name = "wan.auto"; switch_network_list_create(&rfc_list, tmp_name, SWITCH_TRUE, IP_LIST.pool); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Created ip list %s default (allow)\n", tmp_name); switch_network_list_add_cidr(rfc_list, "0.0.0.0/8", SWITCH_FALSE); switch_network_list_add_cidr(rfc_list, "10.0.0.0/8", SWITCH_FALSE); switch_network_list_add_cidr(rfc_list, "172.16.0.0/12", SWITCH_FALSE); switch_network_list_add_cidr(rfc_list, "192.168.0.0/16", SWITCH_FALSE); switch_network_list_add_cidr(rfc_list, "169.254.0.0/16", SWITCH_FALSE); switch_network_list_add_cidr(rfc_list, "fe80::/10", SWITCH_FALSE); switch_core_hash_insert(IP_LIST.hash, tmp_name, rfc_list); tmp_name = "wan_v6.auto"; switch_network_list_create(&rfc_list, tmp_name, SWITCH_TRUE, IP_LIST.pool); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Created ip list %s default (allow)\n", tmp_name); switch_network_list_add_cidr(rfc_list, "0.0.0.0/0", SWITCH_FALSE); switch_network_list_add_cidr(rfc_list, "fe80::/10", SWITCH_FALSE); switch_core_hash_insert(IP_LIST.hash, tmp_name, rfc_list); tmp_name = "wan_v4.auto"; switch_network_list_create(&rfc_list, tmp_name, SWITCH_TRUE, IP_LIST.pool); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Created ip list %s default (allow)\n", tmp_name); switch_network_list_add_cidr(rfc_list, "0.0.0.0/8", SWITCH_FALSE); switch_network_list_add_cidr(rfc_list, "10.0.0.0/8", SWITCH_FALSE); switch_network_list_add_cidr(rfc_list, "172.16.0.0/12", SWITCH_FALSE); switch_network_list_add_cidr(rfc_list, "192.168.0.0/16", SWITCH_FALSE); switch_network_list_add_cidr(rfc_list, "169.254.0.0/16", SWITCH_FALSE); switch_network_list_add_cidr(rfc_list, "::/0", SWITCH_FALSE); switch_core_hash_insert(IP_LIST.hash, tmp_name, rfc_list); tmp_name = "any_v6.auto"; switch_network_list_create(&rfc_list, tmp_name, SWITCH_TRUE, IP_LIST.pool); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Created ip list %s default (allow)\n", tmp_name); switch_network_list_add_cidr(rfc_list, "0.0.0.0/0", SWITCH_FALSE); switch_core_hash_insert(IP_LIST.hash, tmp_name, rfc_list); tmp_name = "any_v4.auto"; switch_network_list_create(&rfc_list, tmp_name, SWITCH_TRUE, IP_LIST.pool); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Created ip list %s default (allow)\n", tmp_name); switch_network_list_add_cidr(rfc_list, "::/0", SWITCH_FALSE); switch_core_hash_insert(IP_LIST.hash, tmp_name, rfc_list); tmp_name = "nat.auto"; switch_network_list_create(&rfc_list, tmp_name, SWITCH_FALSE, IP_LIST.pool); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Created ip list %s default (deny)\n", tmp_name); if (switch_network_list_add_host_mask(rfc_list, guess_ip, guess_mask, SWITCH_FALSE) == SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Adding %s/%s (deny) to list %s\n", guess_ip, guess_mask, tmp_name); } switch_network_list_add_cidr(rfc_list, "10.0.0.0/8", SWITCH_TRUE); switch_network_list_add_cidr(rfc_list, "172.16.0.0/12", SWITCH_TRUE); switch_network_list_add_cidr(rfc_list, "192.168.0.0/16", SWITCH_TRUE); switch_network_list_add_cidr(rfc_list, "100.64.0.0/10", SWITCH_TRUE); switch_core_hash_insert(IP_LIST.hash, tmp_name, rfc_list); tmp_name = "loopback.auto"; switch_network_list_create(&rfc_list, tmp_name, SWITCH_FALSE, IP_LIST.pool); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Created ip list %s default (deny)\n", tmp_name); switch_network_list_add_cidr(rfc_list, "127.0.0.0/8", SWITCH_TRUE); switch_network_list_add_cidr(rfc_list, "::1/128", SWITCH_TRUE); switch_core_hash_insert(IP_LIST.hash, tmp_name, rfc_list); tmp_name = "localnet.auto"; switch_network_list_create(&list, tmp_name, SWITCH_FALSE, IP_LIST.pool); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Created ip list %s default (deny)\n", tmp_name); if (switch_network_list_add_host_mask(list, guess_ip, guess_mask, SWITCH_TRUE) == SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Adding %s/%s (allow) to list %s\n", guess_ip, guess_mask, tmp_name); } switch_core_hash_insert(IP_LIST.hash, tmp_name, list); if ((xml = switch_xml_open_cfg("acl.conf", &cfg, NULL))) { if ((x_lists = switch_xml_child(cfg, "network-lists"))) { for (x_list = switch_xml_child(x_lists, "list"); x_list; x_list = x_list->next) { const char *name = switch_xml_attr(x_list, "name"); const char *dft = switch_xml_attr(x_list, "default"); switch_bool_t default_type = SWITCH_TRUE; if (zstr(name)) { continue; } if (dft) { default_type = switch_true(dft); } if (switch_network_list_create(&list, name, default_type, IP_LIST.pool) != SWITCH_STATUS_SUCCESS) { abort(); } if (reload) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Created ip list %s default (%s)\n", name, default_type ? "allow" : "deny"); } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Created ip list %s default (%s)\n", name, default_type ? "allow" : "deny"); } for (x_node = switch_xml_child(x_list, "node"); x_node; x_node = x_node->next) { const char *cidr = NULL, *host = NULL, *mask = NULL, *domain = NULL; switch_bool_t ok = default_type; const char *type = switch_xml_attr(x_node, "type"); if (type) { ok = switch_true(type); } cidr = switch_xml_attr(x_node, "cidr"); host = switch_xml_attr(x_node, "host"); mask = switch_xml_attr(x_node, "mask"); domain = switch_xml_attr(x_node, "domain"); if (domain) { switch_event_t *my_params = NULL; switch_xml_t x_domain, xml_root; switch_xml_t gt, gts, ut, uts; switch_event_create(&my_params, SWITCH_EVENT_GENERAL); switch_assert(my_params); switch_event_add_header_string(my_params, SWITCH_STACK_BOTTOM, "domain", domain); switch_event_add_header_string(my_params, SWITCH_STACK_BOTTOM, "purpose", "network-list"); if (switch_xml_locate_domain(domain, my_params, &xml_root, &x_domain) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Cannot locate domain %s\n", domain); switch_event_destroy(&my_params); continue; } switch_event_destroy(&my_params); if ((ut = switch_xml_child(x_domain, "users"))) { x_domain = ut; } for (ut = switch_xml_child(x_domain, "user"); ut; ut = ut->next) { const char *user_cidr = switch_xml_attr(ut, "cidr"); const char *id = switch_xml_attr(ut, "id"); if (id && user_cidr) { char *token = switch_mprintf("%s@%s", id, domain); switch_assert(token); switch_network_list_add_cidr_token(list, user_cidr, ok, token); free(token); } } for (gts = switch_xml_child(x_domain, "groups"); gts; gts = gts->next) { for (gt = switch_xml_child(gts, "group"); gt; gt = gt->next) { for (uts = switch_xml_child(gt, "users"); uts; uts = uts->next) { for (ut = switch_xml_child(uts, "user"); ut; ut = ut->next) { const char *user_cidr = switch_xml_attr(ut, "cidr"); const char *id = switch_xml_attr(ut, "id"); if (id && user_cidr) { char *token = switch_mprintf("%s@%s", id, domain); switch_assert(token); switch_network_list_add_cidr_token(list, user_cidr, ok, token); free(token); } } } } } switch_xml_free(xml_root); } else if (cidr) { switch_network_list_add_cidr(list, cidr, ok); } else if (host && mask) { switch_network_list_add_host_mask(list, host, mask, ok); } switch_core_hash_insert(IP_LIST.hash, name, list); } } } switch_xml_free(xml); } switch_mutex_unlock(runtime.global_mutex); } SWITCH_DECLARE(uint32_t) switch_core_max_dtmf_duration(uint32_t duration) { if (duration) { if (duration > SWITCH_MAX_DTMF_DURATION) { duration = SWITCH_MAX_DTMF_DURATION; } if (duration < SWITCH_MIN_DTMF_DURATION) { duration = SWITCH_MIN_DTMF_DURATION; } runtime.max_dtmf_duration = duration; if (duration < runtime.min_dtmf_duration) { runtime.min_dtmf_duration = duration; } } return runtime.max_dtmf_duration; } SWITCH_DECLARE(uint32_t) switch_core_default_dtmf_duration(uint32_t duration) { if (duration) { if (duration < SWITCH_MIN_DTMF_DURATION) { duration = SWITCH_MIN_DTMF_DURATION; } if (duration > SWITCH_MAX_DTMF_DURATION) { duration = SWITCH_MAX_DTMF_DURATION; } runtime.default_dtmf_duration = duration; if (duration < runtime.min_dtmf_duration) { runtime.min_dtmf_duration = duration; } if (duration > runtime.max_dtmf_duration) { runtime.max_dtmf_duration = duration; } } return runtime.default_dtmf_duration; } SWITCH_DECLARE(uint32_t) switch_core_min_dtmf_duration(uint32_t duration) { if (duration) { if (duration < SWITCH_MIN_DTMF_DURATION) { duration = SWITCH_MIN_DTMF_DURATION; } if (duration > SWITCH_MAX_DTMF_DURATION) { duration = SWITCH_MAX_DTMF_DURATION; } runtime.min_dtmf_duration = duration; if (duration > runtime.max_dtmf_duration) { runtime.max_dtmf_duration = duration; } } return runtime.min_dtmf_duration; } SWITCH_DECLARE(switch_status_t) switch_core_thread_set_cpu_affinity(int cpu) { switch_status_t status = SWITCH_STATUS_FALSE; if (cpu > -1) { #ifdef HAVE_CPU_SET_MACROS cpu_set_t set; CPU_ZERO(&set); CPU_SET(cpu, &set); if (!sched_setaffinity(0, sizeof(set), &set)) { status = SWITCH_STATUS_SUCCESS; } #else #if WIN32 if (SetThreadAffinityMask(GetCurrentThread(), (DWORD_PTR) cpu)) { status = SWITCH_STATUS_SUCCESS; } #endif #endif } return status; } #ifdef ENABLE_ZRTP static void switch_core_set_serial(void) { char buf[13] = ""; char path[256]; int fd = -1, write_fd = -1; switch_ssize_t bytes = 0; switch_snprintf(path, sizeof(path), "%s%sfreeswitch.serial", SWITCH_GLOBAL_dirs.conf_dir, SWITCH_PATH_SEPARATOR); if ((fd = open(path, O_RDONLY, 0)) < 0) { char *ip = switch_core_get_variable_dup("local_ip_v4"); uint32_t ipi = 0; switch_byte_t *byte; int i = 0; if (ip) { switch_inet_pton(AF_INET, ip, &ipi); free(ip); ip = NULL; } byte = (switch_byte_t *) & ipi; for (i = 0; i < 8; i += 2) { switch_snprintf(buf + i, sizeof(buf) - i, "%0.2x", *byte); byte++; } switch_stun_random_string(buf + 8, 4, "0123456789abcdef"); if ((write_fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR)) >= 0) { bytes = write(write_fd, buf, sizeof(buf)); bytes++; close(write_fd); } } else { bytes = read(fd, buf, sizeof(buf) - 1); close(fd); } switch_core_set_variable("switch_serial", buf); } #endif SWITCH_DECLARE(int) switch_core_test_flag(int flag) { return switch_test_flag((&runtime), flag); } SWITCH_DECLARE(switch_status_t) switch_core_init(switch_core_flag_t flags, switch_bool_t console, const char **err) { switch_uuid_t uuid; char guess_ip[256]; int mask = 0; struct in_addr in; if (runtime.runlevel > 0) { /* one per customer */ return SWITCH_STATUS_SUCCESS; } memset(&runtime, 0, sizeof(runtime)); gethostname(runtime.hostname, sizeof(runtime.hostname)); runtime.max_db_handles = 50; runtime.db_handle_timeout = 5000000; runtime.event_heartbeat_interval = 20; runtime.runlevel++; runtime.dummy_cng_frame.data = runtime.dummy_data; runtime.dummy_cng_frame.datalen = sizeof(runtime.dummy_data); runtime.dummy_cng_frame.buflen = sizeof(runtime.dummy_data); runtime.dbname = "core"; switch_set_flag((&runtime.dummy_cng_frame), SFF_CNG); switch_set_flag((&runtime), SCF_AUTO_SCHEMAS); switch_set_flag((&runtime), SCF_CLEAR_SQL); switch_set_flag((&runtime), SCF_API_EXPANSION); switch_set_flag((&runtime), SCF_SESSION_THREAD_POOL); #ifdef WIN32 switch_set_flag((&runtime), SCF_THREADED_SYSTEM_EXEC); #endif switch_set_flag((&runtime), SCF_NO_NEW_SESSIONS); runtime.hard_log_level = SWITCH_LOG_DEBUG; runtime.mailer_app = "sendmail"; runtime.mailer_app_args = "-t"; runtime.max_dtmf_duration = SWITCH_MAX_DTMF_DURATION; runtime.default_dtmf_duration = SWITCH_DEFAULT_DTMF_DURATION; runtime.min_dtmf_duration = SWITCH_MIN_DTMF_DURATION; runtime.odbc_dbtype = DBTYPE_DEFAULT; runtime.dbname = NULL; #ifndef WIN32 runtime.cpu_count = sysconf (_SC_NPROCESSORS_ONLN); #else { SYSTEM_INFO sysinfo; GetSystemInfo( &sysinfo ); runtime.cpu_count = sysinfo.dwNumberOfProcessors; } #endif if (!runtime.cpu_count) runtime.cpu_count = 1; if (sqlite3_initialize() != SQLITE_OK) { *err = "FATAL ERROR! Could not initialize SQLite\n"; return SWITCH_STATUS_MEMERR; } /* INIT APR and Create the pool context */ if (apr_initialize() != SWITCH_STATUS_SUCCESS) { *err = "FATAL ERROR! Could not initialize APR\n"; return SWITCH_STATUS_MEMERR; } if (!(runtime.memory_pool = switch_core_memory_init())) { *err = "FATAL ERROR! Could not allocate memory pool\n"; return SWITCH_STATUS_MEMERR; } switch_assert(runtime.memory_pool != NULL); switch_dir_make_recursive(SWITCH_GLOBAL_dirs.base_dir, SWITCH_DEFAULT_DIR_PERMS, runtime.memory_pool); switch_dir_make_recursive(SWITCH_GLOBAL_dirs.mod_dir, SWITCH_DEFAULT_DIR_PERMS, runtime.memory_pool); switch_dir_make_recursive(SWITCH_GLOBAL_dirs.conf_dir, SWITCH_DEFAULT_DIR_PERMS, runtime.memory_pool); switch_dir_make_recursive(SWITCH_GLOBAL_dirs.log_dir, SWITCH_DEFAULT_DIR_PERMS, runtime.memory_pool); switch_dir_make_recursive(SWITCH_GLOBAL_dirs.run_dir, SWITCH_DEFAULT_DIR_PERMS, runtime.memory_pool); switch_dir_make_recursive(SWITCH_GLOBAL_dirs.db_dir, SWITCH_DEFAULT_DIR_PERMS, runtime.memory_pool); switch_dir_make_recursive(SWITCH_GLOBAL_dirs.script_dir, SWITCH_DEFAULT_DIR_PERMS, runtime.memory_pool); switch_dir_make_recursive(SWITCH_GLOBAL_dirs.htdocs_dir, SWITCH_DEFAULT_DIR_PERMS, runtime.memory_pool); switch_dir_make_recursive(SWITCH_GLOBAL_dirs.grammar_dir, SWITCH_DEFAULT_DIR_PERMS, runtime.memory_pool); switch_dir_make_recursive(SWITCH_GLOBAL_dirs.fonts_dir, SWITCH_DEFAULT_DIR_PERMS, runtime.memory_pool); switch_dir_make_recursive(SWITCH_GLOBAL_dirs.images_dir, SWITCH_DEFAULT_DIR_PERMS, runtime.memory_pool); switch_dir_make_recursive(SWITCH_GLOBAL_dirs.recordings_dir, SWITCH_DEFAULT_DIR_PERMS, runtime.memory_pool); switch_dir_make_recursive(SWITCH_GLOBAL_dirs.sounds_dir, SWITCH_DEFAULT_DIR_PERMS, runtime.memory_pool); switch_dir_make_recursive(SWITCH_GLOBAL_dirs.temp_dir, SWITCH_DEFAULT_DIR_PERMS, runtime.memory_pool); switch_dir_make_recursive(SWITCH_GLOBAL_dirs.certs_dir, SWITCH_DEFAULT_DIR_PERMS, runtime.memory_pool); switch_mutex_init(&runtime.uuid_mutex, SWITCH_MUTEX_NESTED, runtime.memory_pool); switch_mutex_init(&runtime.throttle_mutex, SWITCH_MUTEX_NESTED, runtime.memory_pool); switch_mutex_init(&runtime.session_hash_mutex, SWITCH_MUTEX_NESTED, runtime.memory_pool); switch_mutex_init(&runtime.global_mutex, SWITCH_MUTEX_NESTED, runtime.memory_pool); switch_thread_rwlock_create(&runtime.global_var_rwlock, runtime.memory_pool); switch_core_set_globals(); switch_core_session_init(runtime.memory_pool); switch_event_create_plain(&runtime.global_vars, SWITCH_EVENT_CHANNEL_DATA); switch_core_hash_init_case(&runtime.mime_types, SWITCH_FALSE); switch_core_hash_init_case(&runtime.mime_type_exts, SWITCH_FALSE); switch_core_hash_init_case(&runtime.ptimes, SWITCH_FALSE); load_mime_types(); runtime.flags |= flags; runtime.sps_total = 30; *err = NULL; if (console) { runtime.console = stdout; } SSL_library_init(); switch_ssl_init_ssl_locks(); switch_curl_init(); switch_core_set_variable("hostname", runtime.hostname); switch_find_local_ip(guess_ip, sizeof(guess_ip), &mask, AF_INET); switch_core_set_variable("local_ip_v4", guess_ip); in.s_addr = mask; switch_core_set_variable("local_mask_v4", inet_ntoa(in)); switch_find_local_ip(guess_ip, sizeof(guess_ip), NULL, AF_INET6); switch_core_set_variable("local_ip_v6", guess_ip); switch_core_set_variable("base_dir", SWITCH_GLOBAL_dirs.base_dir); switch_core_set_variable("recordings_dir", SWITCH_GLOBAL_dirs.recordings_dir); switch_core_set_variable("sound_prefix", SWITCH_GLOBAL_dirs.sounds_dir); switch_core_set_variable("sounds_dir", SWITCH_GLOBAL_dirs.sounds_dir); switch_core_set_variable("conf_dir", SWITCH_GLOBAL_dirs.conf_dir); switch_core_set_variable("log_dir", SWITCH_GLOBAL_dirs.log_dir); switch_core_set_variable("run_dir", SWITCH_GLOBAL_dirs.run_dir); switch_core_set_variable("db_dir", SWITCH_GLOBAL_dirs.db_dir); switch_core_set_variable("mod_dir", SWITCH_GLOBAL_dirs.mod_dir); switch_core_set_variable("htdocs_dir", SWITCH_GLOBAL_dirs.htdocs_dir); switch_core_set_variable("script_dir", SWITCH_GLOBAL_dirs.script_dir); switch_core_set_variable("temp_dir", SWITCH_GLOBAL_dirs.temp_dir); switch_core_set_variable("grammar_dir", SWITCH_GLOBAL_dirs.grammar_dir); switch_core_set_variable("fonts_dir", SWITCH_GLOBAL_dirs.fonts_dir); switch_core_set_variable("images_dir", SWITCH_GLOBAL_dirs.images_dir); switch_core_set_variable("certs_dir", SWITCH_GLOBAL_dirs.certs_dir); switch_core_set_variable("storage_dir", SWITCH_GLOBAL_dirs.storage_dir); switch_core_set_variable("cache_dir", SWITCH_GLOBAL_dirs.cache_dir); switch_core_set_variable("data_dir", SWITCH_GLOBAL_dirs.data_dir); switch_core_set_variable("localstate_dir", SWITCH_GLOBAL_dirs.localstate_dir); #ifdef ENABLE_ZRTP switch_core_set_serial(); #endif switch_console_init(runtime.memory_pool); switch_event_init(runtime.memory_pool); switch_channel_global_init(runtime.memory_pool); if (switch_xml_init(runtime.memory_pool, err) != SWITCH_STATUS_SUCCESS) { apr_terminate(); return SWITCH_STATUS_MEMERR; } if (switch_test_flag((&runtime), SCF_USE_AUTO_NAT)) { switch_nat_init(runtime.memory_pool, switch_test_flag((&runtime), SCF_USE_NAT_MAPPING)); } switch_log_init(runtime.memory_pool, runtime.colorize_console); runtime.tipping_point = 0; runtime.timer_affinity = -1; runtime.microseconds_per_tick = 20000; if (flags & SCF_MINIMAL) return SWITCH_STATUS_SUCCESS; switch_load_core_config("switch.conf"); switch_core_state_machine_init(runtime.memory_pool); if (switch_core_sqldb_start(runtime.memory_pool, switch_test_flag((&runtime), SCF_USE_SQL) ? SWITCH_TRUE : SWITCH_FALSE) != SWITCH_STATUS_SUCCESS) { *err = "Error activating database"; return SWITCH_STATUS_FALSE; } switch_core_media_init(); switch_scheduler_task_thread_start(); switch_nat_late_init(); switch_rtp_init(runtime.memory_pool); runtime.running = 1; runtime.initiated = switch_mono_micro_time_now(); switch_scheduler_add_task(switch_epoch_time_now(NULL), heartbeat_callback, "heartbeat", "core", 0, NULL, SSHF_NONE | SSHF_NO_DEL); switch_scheduler_add_task(switch_epoch_time_now(NULL), check_ip_callback, "check_ip", "core", 0, NULL, SSHF_NONE | SSHF_NO_DEL | SSHF_OWN_THREAD); switch_uuid_get(&uuid); switch_uuid_format(runtime.uuid_str, &uuid); switch_core_set_variable("core_uuid", runtime.uuid_str); return SWITCH_STATUS_SUCCESS; } #ifdef TRAP_BUS static void handle_SIGBUS(int sig) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG1, "Sig BUS!\n"); return; } #endif static void handle_SIGHUP(int sig) { if (sig) { switch_event_t *event; if (switch_event_create(&event, SWITCH_EVENT_TRAP) == SWITCH_STATUS_SUCCESS) { switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Trapped-Signal", "HUP"); switch_event_fire(&event); } } return; } SWITCH_DECLARE(uint32_t) switch_default_ptime(const char *name, uint32_t number) { uint32_t *p; if ((p = switch_core_hash_find(runtime.ptimes, name))) { return *p; } return 20; } SWITCH_DECLARE(uint32_t) switch_default_rate(const char *name, uint32_t number) { if (!strcasecmp(name, "opus")) { return 48000; } else if (!strncasecmp(name, "h26", 3)) { // h26x return 90000; } else if (!strncasecmp(name, "vp", 2)) { // vp8, vp9 return 90000; } return 8000; } static uint32_t d_30 = 30; static void switch_load_core_config(const char *file) { switch_xml_t xml = NULL, cfg = NULL; switch_core_hash_insert(runtime.ptimes, "ilbc", &d_30); switch_core_hash_insert(runtime.ptimes, "isac", &d_30); switch_core_hash_insert(runtime.ptimes, "G723", &d_30); if ((xml = switch_xml_open_cfg(file, &cfg, NULL))) { switch_xml_t settings, param; if ((settings = switch_xml_child(cfg, "default-ptimes"))) { for (param = switch_xml_child(settings, "codec"); param; param = param->next) { const char *var = switch_xml_attr_soft(param, "name"); const char *val = switch_xml_attr_soft(param, "ptime"); if (!zstr(var) && !zstr(val)) { uint32_t *p; uint32_t v = switch_atoul(val); if (!strcasecmp(var, "G723") || !strcasecmp(var, "iLBC")) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Error adding %s, defaults cannot be changed\n", var); continue; } if (v == 0) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Error adding %s, invalid ptime\n", var); continue; } p = switch_core_alloc(runtime.memory_pool, sizeof(*p)); *p = v; switch_core_hash_insert(runtime.ptimes, var, p); } } } if ((settings = switch_xml_child(cfg, "settings"))) { for (param = switch_xml_child(settings, "param"); param; param = param->next) { const char *var = switch_xml_attr_soft(param, "name"); const char *val = switch_xml_attr_soft(param, "value"); if (!strcasecmp(var, "loglevel")) { int level; if (*val > 47 && *val < 58) { level = atoi(val); } else { level = switch_log_str2level(val); } if (level != SWITCH_LOG_INVALID) { switch_core_session_ctl(SCSC_LOGLEVEL, &level); } #ifdef HAVE_SETRLIMIT } else if (!strcasecmp(var, "dump-cores") && switch_true(val)) { struct rlimit rlp; memset(&rlp, 0, sizeof(rlp)); rlp.rlim_cur = RLIM_INFINITY; rlp.rlim_max = RLIM_INFINITY; setrlimit(RLIMIT_CORE, &rlp); #endif } else if (!strcasecmp(var, "debug-level")) { int tmp = atoi(val); if (tmp > -1 && tmp < 11) { switch_core_session_ctl(SCSC_DEBUG_LEVEL, &tmp); } } else if (!strcasecmp(var, "max-db-handles")) { long tmp = atol(val); if (tmp > 4 && tmp < 5001) { runtime.max_db_handles = (uint32_t) tmp; } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "max-db-handles must be between 5 and 5000\n"); } } else if (!strcasecmp(var, "db-handle-timeout")) { long tmp = atol(val); if (tmp > 0 && tmp < 5001) { runtime.db_handle_timeout = (uint32_t) tmp * 1000000; } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "db-handle-timeout must be between 1 and 5000\n"); } } else if (!strcasecmp(var, "event-heartbeat-interval")) { long tmp = atol(val); if (tmp > 0) { runtime.event_heartbeat_interval = (uint32_t) tmp; } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "heartbeat-interval must be a greater than 0\n"); } } else if (!strcasecmp(var, "multiple-registrations")) { runtime.multiple_registrations = switch_true(val); } else if (!strcasecmp(var, "auto-create-schemas")) { if (switch_true(val)) { switch_set_flag((&runtime), SCF_AUTO_SCHEMAS); } else { switch_clear_flag((&runtime), SCF_AUTO_SCHEMAS); } } else if (!strcasecmp(var, "session-thread-pool")) { if (switch_true(val)) { switch_set_flag((&runtime), SCF_SESSION_THREAD_POOL); } else { switch_clear_flag((&runtime), SCF_SESSION_THREAD_POOL); } } else if (!strcasecmp(var, "auto-clear-sql")) { if (switch_true(val)) { switch_set_flag((&runtime), SCF_CLEAR_SQL); } else { switch_clear_flag((&runtime), SCF_CLEAR_SQL); } } else if (!strcasecmp(var, "api-expansion")) { if (switch_true(val)) { switch_set_flag((&runtime), SCF_API_EXPANSION); } else { switch_clear_flag((&runtime), SCF_API_EXPANSION); } } else if (!strcasecmp(var, "enable-early-hangup") && switch_true(val)) { switch_set_flag((&runtime), SCF_EARLY_HANGUP); } else if (!strcasecmp(var, "colorize-console") && switch_true(val)) { runtime.colorize_console = SWITCH_TRUE; } else if (!strcasecmp(var, "core-db-pre-trans-execute") && !zstr(val)) { runtime.core_db_pre_trans_execute = switch_core_strdup(runtime.memory_pool, val); } else if (!strcasecmp(var, "core-db-post-trans-execute") && !zstr(val)) { runtime.core_db_post_trans_execute = switch_core_strdup(runtime.memory_pool, val); } else if (!strcasecmp(var, "core-db-inner-pre-trans-execute") && !zstr(val)) { runtime.core_db_inner_pre_trans_execute = switch_core_strdup(runtime.memory_pool, val); } else if (!strcasecmp(var, "core-db-inner-post-trans-execute") && !zstr(val)) { runtime.core_db_inner_post_trans_execute = switch_core_strdup(runtime.memory_pool, val); } else if (!strcasecmp(var, "dialplan-timestamps")) { if (switch_true(val)) { switch_set_flag((&runtime), SCF_DIALPLAN_TIMESTAMPS); } else { switch_clear_flag((&runtime), SCF_DIALPLAN_TIMESTAMPS); } } else if (!strcasecmp(var, "mailer-app") && !zstr(val)) { runtime.mailer_app = switch_core_strdup(runtime.memory_pool, val); } else if (!strcasecmp(var, "mailer-app-args") && val) { runtime.mailer_app_args = switch_core_strdup(runtime.memory_pool, val); } else if (!strcasecmp(var, "sessions-per-second") && !zstr(val)) { switch_core_sessions_per_second(atoi(val)); } else if (!strcasecmp(var, "max-dtmf-duration") && !zstr(val)) { int tmp = atoi(val); if (tmp > 0) { switch_core_max_dtmf_duration((uint32_t) tmp); } } else if (!strcasecmp(var, "min-dtmf-duration") && !zstr(val)) { int tmp = atoi(val); if (tmp > 0) { switch_core_min_dtmf_duration((uint32_t) tmp); } } else if (!strcasecmp(var, "default-dtmf-duration") && !zstr(val)) { int tmp = atoi(val); if (tmp > 0) { switch_core_default_dtmf_duration((uint32_t) tmp); } } else if (!strcasecmp(var, "enable-use-system-time")) { switch_time_set_use_system_time(switch_true(val)); } else if (!strcasecmp(var, "enable-monotonic-timing")) { switch_time_set_monotonic(switch_true(val)); } else if (!strcasecmp(var, "enable-softtimer-timerfd")) { int ival = 0; if (val) { if (switch_true(val)) { ival = 2; } else { if (strcasecmp(val, "broadcast")) { ival = 1; } else if (strcasecmp(val, "fd-per-timer")) { ival = 2; } } } switch_time_set_timerfd(ival); } else if (!strcasecmp(var, "enable-clock-nanosleep")) { switch_time_set_nanosleep(switch_true(val)); } else if (!strcasecmp(var, "enable-cond-yield")) { switch_time_set_cond_yield(switch_true(val)); } else if (!strcasecmp(var, "enable-timer-matrix")) { switch_time_set_matrix(switch_true(val)); } else if (!strcasecmp(var, "max-sessions") && !zstr(val)) { switch_core_session_limit(atoi(val)); } else if (!strcasecmp(var, "verbose-channel-events") && !zstr(val)) { int v = switch_true(val); if (v) { switch_set_flag((&runtime), SCF_VERBOSE_EVENTS); } else { switch_clear_flag((&runtime), SCF_VERBOSE_EVENTS); } } else if (!strcasecmp(var, "threaded-system-exec") && !zstr(val)) { #ifdef WIN32 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "threaded-system-exec is not implemented on this platform\n"); #else int v = switch_true(val); if (v) { switch_set_flag((&runtime), SCF_THREADED_SYSTEM_EXEC); } else { switch_clear_flag((&runtime), SCF_THREADED_SYSTEM_EXEC); } #endif } else if (!strcasecmp(var, "min-idle-cpu") && !zstr(val)) { switch_core_min_idle_cpu(atof(val)); } else if (!strcasecmp(var, "tipping-point") && !zstr(val)) { runtime.tipping_point = atoi(val); } else if (!strcasecmp(var, "cpu-idle-smoothing-depth") && !zstr(val)) { runtime.cpu_idle_smoothing_depth = atoi(val); } else if (!strcasecmp(var, "events-use-dispatch") && !zstr(val)) { runtime.events_use_dispatch = switch_true(val); } else if (!strcasecmp(var, "initial-event-threads") && !zstr(val)) { int tmp; if (!runtime.events_use_dispatch) { runtime.events_use_dispatch = 1; switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Implicitly setting events-use-dispatch based on usage of this initial-event-threads parameter.\n"); } tmp = atoi(val); if (tmp > runtime.cpu_count / 2) { tmp = runtime.cpu_count / 2; switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "This value cannot be higher than %d so setting it to that value\n", runtime.cpu_count / 2); } if (tmp < 1) { tmp = 1; switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "This value cannot be lower than 1 so setting it to that level\n"); } switch_event_launch_dispatch_threads(tmp); } else if (!strcasecmp(var, "1ms-timer") && switch_true(val)) { runtime.microseconds_per_tick = 1000; } else if (!strcasecmp(var, "timer-affinity") && !zstr(val)) { if (!strcasecmp(val, "disabled")) { runtime.timer_affinity = -1; } else { runtime.timer_affinity = atoi(val); } } else if (!strcasecmp(var, "rtp-start-port") && !zstr(val)) { switch_rtp_set_start_port((switch_port_t) atoi(val)); } else if (!strcasecmp(var, "rtp-end-port") && !zstr(val)) { switch_rtp_set_end_port((switch_port_t) atoi(val)); } else if (!strcasecmp(var, "rtp-port-usage-robustness") && switch_true(val)) { runtime.port_alloc_flags |= SPF_ROBUST_UDP; } else if (!strcasecmp(var, "core-db-name") && !zstr(val)) { runtime.dbname = switch_core_strdup(runtime.memory_pool, val); } else if (!strcasecmp(var, "core-db-dsn") && !zstr(val)) { if (switch_odbc_available() || switch_pgsql_available()) { runtime.odbc_dsn = switch_core_strdup(runtime.memory_pool, val); } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "ODBC AND PGSQL ARE NOT AVAILABLE!\n"); } } else if (!strcasecmp(var, "core-non-sqlite-db-required") && !zstr(val)) { switch_set_flag((&runtime), SCF_CORE_NON_SQLITE_DB_REQ); } else if (!strcasecmp(var, "core-dbtype") && !zstr(val)) { if (!strcasecmp(val, "MSSQL")) { runtime.odbc_dbtype = DBTYPE_MSSQL; } else { runtime.odbc_dbtype = DBTYPE_DEFAULT; } #ifdef ENABLE_ZRTP } else if (!strcasecmp(var, "rtp-enable-zrtp")) { switch_core_set_variable("zrtp_enabled", val); #endif } else if (!strcasecmp(var, "switchname") && !zstr(val)) { runtime.switchname = switch_core_strdup(runtime.memory_pool, val); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Set switchname to %s\n", runtime.switchname); } else if (!strcasecmp(var, "rtp-retain-crypto-keys")) { if (switch_true(val)) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "rtp-retain-crypto-keys enabled. Could be used to decrypt secure media.\n"); } switch_core_set_variable("rtp_retain_crypto_keys", val); } } } if ((settings = switch_xml_child(cfg, "variables"))) { for (param = switch_xml_child(settings, "variable"); param; param = param->next) { const char *var = switch_xml_attr_soft(param, "name"); const char *val = switch_xml_attr_soft(param, "value"); if (var && val) { switch_core_set_variable(var, val); } } } switch_xml_free(xml); } } SWITCH_DECLARE(const char *) switch_core_banner(void) { return ("\n" ".=============================================================.\n" "| _____ ______ _____ _____ ____ _ _ |\n" "| | ___| __ ___ ___/ ___\\ \\ / /_ _|_ _/ ___| | | | |\n" "| | |_ | '__/ _ \\/ _ \\___ \\\\ \\ /\\ / / | | | || | | |_| | |\n" "| | _|| | | __/ __/___) |\\ V V / | | | || |___| _ | |\n" "| |_| |_| \\___|\\___|____/ \\_/\\_/ |___| |_| \\____|_| |_| |\n" "| |\n" ".=============================================================." "\n" "| Anthony Minessale II, Michael Jerris, Brian West, Others |\n" "| FreeSWITCH (http://www.freeswitch.org) |\n" "| Paypal Donations Appreciated: paypal@freeswitch.org |\n" "| Brought to you by ClueCon http://www.cluecon.com/ |\n" ".=============================================================.\n" "\n"); } SWITCH_DECLARE(switch_status_t) switch_core_init_and_modload(switch_core_flag_t flags, switch_bool_t console, const char **err) { switch_event_t *event; char *cmd; int x = 0; const char *use = NULL; #include "cc.h" if (switch_core_init(flags, console, err) != SWITCH_STATUS_SUCCESS) { return SWITCH_STATUS_GENERR; } if (runtime.runlevel > 1) { /* one per customer */ return SWITCH_STATUS_SUCCESS; } runtime.runlevel++; runtime.events_use_dispatch = 1; switch_core_set_signal_handlers(); switch_load_network_lists(SWITCH_FALSE); switch_msrp_init(); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Bringing up environment.\n"); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Loading Modules.\n"); if (switch_loadable_module_init(SWITCH_TRUE) != SWITCH_STATUS_SUCCESS) { *err = "Cannot load modules"; switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Error: %s\n", *err); return SWITCH_STATUS_GENERR; } switch_load_network_lists(SWITCH_FALSE); switch_load_core_config("post_load_switch.conf"); switch_core_set_signal_handlers(); if (switch_event_create(&event, SWITCH_EVENT_STARTUP) == SWITCH_STATUS_SUCCESS) { switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Event-Info", "System Ready"); switch_event_fire(&event); } switch_core_screen_size(&x, NULL); use = (x > 100) ? cc : cc_s; #ifdef WIN32 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "%s%s\n\n", switch_core_banner(), use); #else switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "%s%s%s%s%s%s\n\n", SWITCH_SEQ_DEFAULT_COLOR, SWITCH_SEQ_FYELLOW, SWITCH_SEQ_BBLUE, switch_core_banner(), use, SWITCH_SEQ_DEFAULT_COLOR); #endif switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "\nFreeSWITCH Version %s (%s)\n\nFreeSWITCH Started\nMax Sessions [%u]\nSession Rate [%d]\nSQL [%s]\n", switch_version_full(), switch_version_revision_human(), switch_core_session_limit(0), switch_core_sessions_per_second(0), switch_test_flag((&runtime), SCF_USE_SQL) ? "Enabled" : "Disabled"); if (x < 160) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "\n[This app Best viewed at 160x60 or more..]\n"); } switch_clear_flag((&runtime), SCF_NO_NEW_SESSIONS); if ((cmd = switch_core_get_variable_dup("api_on_startup"))) { switch_stream_handle_t stream = { 0 }; SWITCH_STANDARD_STREAM(stream); switch_console_execute(cmd, 0, &stream); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Startup command [%s] executed. Output:\n%s\n", cmd, (char *)stream.data); free(stream.data); free(cmd); } return SWITCH_STATUS_SUCCESS; } SWITCH_DECLARE(void) switch_core_measure_time(switch_time_t total_ms, switch_core_time_duration_t *duration) { switch_time_t temp = total_ms / 1000; memset(duration, 0, sizeof(*duration)); duration->mms = (uint32_t) (total_ms % 1000); duration->ms = (uint32_t) (temp % 1000); temp = temp / 1000; duration->sec = (uint32_t) (temp % 60); temp = temp / 60; duration->min = (uint32_t) (temp % 60); temp = temp / 60; duration->hr = (uint32_t) (temp % 24); temp = temp / 24; duration->day = (uint32_t) (temp % 365); duration->yr = (uint32_t) (temp / 365); } SWITCH_DECLARE(switch_time_t) switch_core_uptime(void) { return switch_mono_micro_time_now() - runtime.initiated; } #ifdef _MSC_VER static void win_shutdown(void) { HANDLE shutdown_event; char path[512]; /* for windows we need the event to signal for shutting down a background FreeSWITCH */ snprintf(path, sizeof(path), "Global\\Freeswitch.%d", getpid()); /* open the event so we can signal it */ shutdown_event = OpenEvent(EVENT_MODIFY_STATE, FALSE, path); if (shutdown_event) { /* signal the event to shutdown */ SetEvent(shutdown_event); /* cleanup */ CloseHandle(shutdown_event); } } #endif SWITCH_DECLARE(void) switch_core_set_signal_handlers(void) { /* set signal handlers */ signal(SIGINT, SIG_IGN); #ifdef SIGPIPE signal(SIGPIPE, SIG_IGN); #endif #ifdef SIGALRM signal(SIGALRM, SIG_IGN); #endif #ifdef SIGQUIT signal(SIGQUIT, SIG_IGN); #endif #ifdef SIGPOLL signal(SIGPOLL, SIG_IGN); #endif #ifdef SIGIO signal(SIGIO, SIG_IGN); #endif #ifdef TRAP_BUS signal(SIGBUS, handle_SIGBUS); #endif #ifdef SIGUSR1 signal(SIGUSR1, handle_SIGHUP); #endif signal(SIGHUP, handle_SIGHUP); } SWITCH_DECLARE(uint32_t) switch_core_debug_level(void) { return runtime.debug_level; } SWITCH_DECLARE(int32_t) switch_core_sps(void) { return runtime.sps; } SWITCH_DECLARE(int32_t) switch_core_sps_last(void) { return runtime.sps_last; } SWITCH_DECLARE(int32_t) switch_core_sps_peak(void) { return runtime.sps_peak; } SWITCH_DECLARE(int32_t) switch_core_sps_peak_fivemin(void) { return runtime.sps_peak_fivemin; } SWITCH_DECLARE(int32_t) switch_core_sessions_peak(void) { return runtime.sessions_peak; } SWITCH_DECLARE(int32_t) switch_core_sessions_peak_fivemin(void) { return runtime.sessions_peak_fivemin; } SWITCH_DECLARE(int32_t) switch_core_session_ctl(switch_session_ctl_t cmd, void *val) { int *intval = (int *) val; int oldintval = 0, newintval = 0; if (intval) { oldintval = *intval; } if (switch_test_flag((&runtime), SCF_SHUTTING_DOWN)) { return -1; } switch (cmd) { case SCSC_RECOVER: { char *arg = (char *) val; char *tech = NULL, *prof = NULL; int r, flush = 0; if (!zstr(arg)) { tech = strdup(arg); if ((prof = strchr(tech, ':'))) { *prof++ = '\0'; } if (!strcasecmp(tech, "flush")) { flush++; tech = NULL; if (prof) { tech = prof; if ((prof = strchr(tech, ':'))) { *prof++ = '\0'; } } } } if (flush) { switch_core_recovery_flush(tech, prof); r = -1; } else { r = switch_core_recovery_recover(tech, prof); } switch_safe_free(tech); return r; } break; case SCSC_DEBUG_SQL: { if (switch_test_flag((&runtime), SCF_DEBUG_SQL)) { switch_clear_flag((&runtime), SCF_DEBUG_SQL); newintval = 0; } else { switch_set_flag((&runtime), SCF_DEBUG_SQL); newintval = 1; } } break; case SCSC_VERBOSE_EVENTS: if (intval) { if (oldintval > -1) { if (oldintval) { switch_set_flag((&runtime), SCF_VERBOSE_EVENTS); } else { switch_clear_flag((&runtime), SCF_VERBOSE_EVENTS); } } newintval = switch_test_flag((&runtime), SCF_VERBOSE_EVENTS); } break; case SCSC_API_EXPANSION: if (intval) { if (oldintval > -1) { if (oldintval) { switch_set_flag((&runtime), SCF_API_EXPANSION); } else { switch_clear_flag((&runtime), SCF_API_EXPANSION); } } newintval = switch_test_flag((&runtime), SCF_API_EXPANSION); } break; case SCSC_THREADED_SYSTEM_EXEC: if (intval) { if (oldintval > -1) { if (oldintval) { switch_set_flag((&runtime), SCF_THREADED_SYSTEM_EXEC); } else { switch_clear_flag((&runtime), SCF_THREADED_SYSTEM_EXEC); } } newintval = switch_test_flag((&runtime), SCF_THREADED_SYSTEM_EXEC); } break; case SCSC_CALIBRATE_CLOCK: switch_time_calibrate_clock(); break; case SCSC_FLUSH_DB_HANDLES: switch_cache_db_flush_handles(); break; case SCSC_SEND_SIGHUP: handle_SIGHUP(1); break; case SCSC_SYNC_CLOCK: switch_time_sync(); newintval = 0; break; case SCSC_SYNC_CLOCK_WHEN_IDLE: newintval = switch_core_session_sync_clock(); break; case SCSC_SQL: if (oldintval) { switch_core_sqldb_resume(); } else { switch_core_sqldb_pause(); } break; case SCSC_PAUSE_ALL: if (oldintval) { switch_set_flag((&runtime), SCF_NO_NEW_SESSIONS); } else { switch_clear_flag((&runtime), SCF_NO_NEW_SESSIONS); } break; case SCSC_PAUSE_INBOUND: if (oldintval) { switch_set_flag((&runtime), SCF_NO_NEW_INBOUND_SESSIONS); } else { switch_clear_flag((&runtime), SCF_NO_NEW_INBOUND_SESSIONS); } break; case SCSC_PAUSE_OUTBOUND: if (oldintval) { switch_set_flag((&runtime), SCF_NO_NEW_OUTBOUND_SESSIONS); } else { switch_clear_flag((&runtime), SCF_NO_NEW_OUTBOUND_SESSIONS); } break; case SCSC_HUPALL: switch_core_session_hupall(SWITCH_CAUSE_MANAGER_REQUEST); break; case SCSC_CANCEL_SHUTDOWN: switch_clear_flag((&runtime), SCF_SHUTDOWN_REQUESTED); break; case SCSC_SAVE_HISTORY: switch_console_save_history(); break; case SCSC_CRASH: switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Declinatio Mortuus Obfirmo!\n"); switch_console_save_history(); abort(); break; case SCSC_SHUTDOWN_NOW: switch_console_save_history(); exit(0); break; case SCSC_REINCARNATE_NOW: switch_console_save_history(); exit(SWITCH_STATUS_RESTART); break; case SCSC_SHUTDOWN_ELEGANT: case SCSC_SHUTDOWN_ASAP: { int x = 19; uint32_t count; switch_set_flag((&runtime), SCF_SHUTDOWN_REQUESTED); if (cmd == SCSC_SHUTDOWN_ASAP) { switch_set_flag((&runtime), SCF_NO_NEW_SESSIONS); } while (runtime.running && switch_test_flag((&runtime), SCF_SHUTDOWN_REQUESTED) && (count = switch_core_session_count())) { switch_yield(500000); if (++x == 20) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Shutdown in progress, %u session(s) remain.\nShutting down %s\n", count, cmd == SCSC_SHUTDOWN_ASAP ? "ASAP" : "once there are no active calls."); x = 0; } } if (switch_test_flag((&runtime), SCF_SHUTDOWN_REQUESTED)) { switch_set_flag((&runtime), SCF_NO_NEW_SESSIONS); #ifdef _MSC_VER win_shutdown(); #endif if (oldintval) { switch_set_flag((&runtime), SCF_RESTART); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Restarting\n"); } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Shutting down\n"); #ifdef _MSC_VER fclose(stdin); #endif } runtime.running = 0; } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Shutdown Cancelled\n"); switch_clear_flag((&runtime), SCF_NO_NEW_SESSIONS); } } break; case SCSC_PAUSE_CHECK: newintval = !!(switch_test_flag((&runtime), SCF_NO_NEW_SESSIONS) == SCF_NO_NEW_SESSIONS); break; case SCSC_PAUSE_INBOUND_CHECK: newintval = !!switch_test_flag((&runtime), SCF_NO_NEW_INBOUND_SESSIONS); break; case SCSC_PAUSE_OUTBOUND_CHECK: newintval = !!switch_test_flag((&runtime), SCF_NO_NEW_OUTBOUND_SESSIONS); break; case SCSC_READY_CHECK: newintval = switch_core_ready(); break; case SCSC_SHUTDOWN_CHECK: newintval = !!switch_test_flag((&runtime), SCF_SHUTDOWN_REQUESTED); break; case SCSC_SHUTDOWN: #ifdef _MSC_VER win_shutdown(); #endif if (oldintval) { switch_set_flag((&runtime), SCF_RESTART); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Restarting\n"); } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Shutting down\n"); #ifdef _MSC_VER fclose(stdin); #endif } runtime.running = 0; break; case SCSC_CHECK_RUNNING: newintval = runtime.running; break; case SCSC_LOGLEVEL: if (oldintval > -1) { runtime.hard_log_level = oldintval; } if (runtime.hard_log_level > SWITCH_LOG_DEBUG) { runtime.hard_log_level = SWITCH_LOG_DEBUG; } newintval = runtime.hard_log_level; break; case SCSC_DEBUG_LEVEL: if (oldintval > -1) { if (oldintval > 10) newintval = 10; runtime.debug_level = oldintval; } newintval = runtime.debug_level; break; case SCSC_MIN_IDLE_CPU: { double *dval = (double *) val; if (dval) { *dval = switch_core_min_idle_cpu(*dval); } intval = NULL; } break; case SCSC_MAX_SESSIONS: newintval = switch_core_session_limit(oldintval); break; case SCSC_LAST_SPS: newintval = runtime.sps_last; break; case SCSC_SPS_PEAK: if (oldintval == -1) { runtime.sps_peak = 0; } newintval = runtime.sps_peak; break; case SCSC_SPS_PEAK_FIVEMIN: newintval = runtime.sps_peak_fivemin; break; case SCSC_SESSIONS_PEAK: newintval = runtime.sessions_peak; break; case SCSC_SESSIONS_PEAK_FIVEMIN: newintval = runtime.sessions_peak_fivemin; break; case SCSC_MAX_DTMF_DURATION: newintval = switch_core_max_dtmf_duration(oldintval); break; case SCSC_MIN_DTMF_DURATION: newintval = switch_core_min_dtmf_duration(oldintval); break; case SCSC_DEFAULT_DTMF_DURATION: newintval = switch_core_default_dtmf_duration(oldintval); break; case SCSC_SPS: switch_mutex_lock(runtime.throttle_mutex); if (oldintval > 0) { runtime.sps_total = oldintval; } newintval = runtime.sps_total; switch_mutex_unlock(runtime.throttle_mutex); break; case SCSC_RECLAIM: switch_core_memory_reclaim_all(); newintval = 0; break; } if (intval) { *intval = newintval; } return 0; } SWITCH_DECLARE(switch_core_flag_t) switch_core_flags(void) { return runtime.flags; } SWITCH_DECLARE(switch_bool_t) switch_core_running(void) { return runtime.running ? SWITCH_TRUE : SWITCH_FALSE; } SWITCH_DECLARE(switch_bool_t) switch_core_ready(void) { return (switch_test_flag((&runtime), SCF_SHUTTING_DOWN) || switch_test_flag((&runtime), SCF_NO_NEW_SESSIONS) == SCF_NO_NEW_SESSIONS) ? SWITCH_FALSE : SWITCH_TRUE; } SWITCH_DECLARE(switch_bool_t) switch_core_ready_inbound(void) { return (switch_test_flag((&runtime), SCF_SHUTTING_DOWN) || switch_test_flag((&runtime), SCF_NO_NEW_INBOUND_SESSIONS)) ? SWITCH_FALSE : SWITCH_TRUE; } SWITCH_DECLARE(switch_bool_t) switch_core_ready_outbound(void) { return (switch_test_flag((&runtime), SCF_SHUTTING_DOWN) || switch_test_flag((&runtime), SCF_NO_NEW_OUTBOUND_SESSIONS)) ? SWITCH_FALSE : SWITCH_TRUE; } SWITCH_DECLARE(switch_status_t) switch_core_destroy(void) { switch_event_t *event; if (switch_event_create(&event, SWITCH_EVENT_SHUTDOWN) == SWITCH_STATUS_SUCCESS) { switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Event-Info", "System Shutting Down"); switch_event_fire(&event); } switch_set_flag((&runtime), SCF_NO_NEW_SESSIONS); switch_set_flag((&runtime), SCF_SHUTTING_DOWN); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "End existing sessions\n"); switch_core_session_hupall(SWITCH_CAUSE_SYSTEM_SHUTDOWN); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Clean up modules.\n"); switch_loadable_module_shutdown(); switch_ssl_destroy_ssl_locks(); if (switch_test_flag((&runtime), SCF_USE_SQL)) { switch_core_sqldb_stop(); } switch_scheduler_task_thread_stop(); switch_rtp_shutdown(); switch_msrp_destroy(); if (switch_test_flag((&runtime), SCF_USE_AUTO_NAT)) { switch_nat_shutdown(); } switch_xml_destroy(); switch_console_shutdown(); switch_channel_global_uninit(); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Closing Event Engine.\n"); switch_event_shutdown(); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Finalizing Shutdown.\n"); switch_log_shutdown(); switch_core_session_uninit(); switch_core_unset_variables(); switch_core_memory_stop(); if (runtime.console && runtime.console != stdout && runtime.console != stderr) { fclose(runtime.console); runtime.console = NULL; } switch_safe_free(SWITCH_GLOBAL_dirs.base_dir); switch_safe_free(SWITCH_GLOBAL_dirs.mod_dir); switch_safe_free(SWITCH_GLOBAL_dirs.conf_dir); switch_safe_free(SWITCH_GLOBAL_dirs.log_dir); switch_safe_free(SWITCH_GLOBAL_dirs.db_dir); switch_safe_free(SWITCH_GLOBAL_dirs.script_dir); switch_safe_free(SWITCH_GLOBAL_dirs.htdocs_dir); switch_safe_free(SWITCH_GLOBAL_dirs.grammar_dir); switch_safe_free(SWITCH_GLOBAL_dirs.fonts_dir); switch_safe_free(SWITCH_GLOBAL_dirs.images_dir); switch_safe_free(SWITCH_GLOBAL_dirs.storage_dir); switch_safe_free(SWITCH_GLOBAL_dirs.cache_dir); switch_safe_free(SWITCH_GLOBAL_dirs.recordings_dir); switch_safe_free(SWITCH_GLOBAL_dirs.sounds_dir); switch_safe_free(SWITCH_GLOBAL_dirs.run_dir); switch_safe_free(SWITCH_GLOBAL_dirs.temp_dir); switch_safe_free(SWITCH_GLOBAL_dirs.data_dir); switch_safe_free(SWITCH_GLOBAL_dirs.localstate_dir); switch_event_destroy(&runtime.global_vars); switch_core_hash_destroy(&runtime.ptimes); switch_core_hash_destroy(&runtime.mime_types); switch_core_hash_destroy(&runtime.mime_type_exts); if (IP_LIST.hash) { switch_core_hash_destroy(&IP_LIST.hash); } if (IP_LIST.pool) { switch_core_destroy_memory_pool(&IP_LIST.pool); } switch_core_media_deinit(); if (runtime.memory_pool) { apr_pool_destroy(runtime.memory_pool); apr_terminate(); } sqlite3_shutdown(); return switch_test_flag((&runtime), SCF_RESTART) ? SWITCH_STATUS_RESTART : SWITCH_STATUS_SUCCESS; } SWITCH_DECLARE(switch_status_t) switch_core_management_exec(char *relative_oid, switch_management_action_t action, char *data, switch_size_t datalen) { const switch_management_interface_t *ptr; switch_status_t status = SWITCH_STATUS_FALSE; if ((ptr = switch_loadable_module_get_management_interface(relative_oid))) { status = ptr->management_function(relative_oid, action, data, datalen); } return status; } SWITCH_DECLARE(void) switch_core_memory_reclaim_all(void) { switch_core_memory_reclaim_logger(); switch_core_memory_reclaim_events(); switch_core_memory_reclaim(); } struct system_thread_handle { const char *cmd; switch_thread_cond_t *cond; switch_mutex_t *mutex; switch_memory_pool_t *pool; int ret; int *fds; }; static void *SWITCH_THREAD_FUNC system_thread(switch_thread_t *thread, void *obj) { struct system_thread_handle *sth = (struct system_thread_handle *) obj; #if defined(HAVE_SETRLIMIT) && !defined(__FreeBSD__) struct rlimit rlim; struct rlimit rlim_save; memset(&rlim, 0, sizeof(rlim)); getrlimit(RLIMIT_STACK, &rlim); memset(&rlim_save, 0, sizeof(rlim_save)); getrlimit(RLIMIT_STACK, &rlim_save); rlim.rlim_cur = rlim.rlim_max; if (setrlimit(RLIMIT_STACK, &rlim) < 0) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Setting stack size failed! (%s)\n", strerror(errno)); } #endif if (sth->fds) { dup2(sth->fds[1], STDOUT_FILENO); } sth->ret = system(sth->cmd); #if defined(HAVE_SETRLIMIT) && !defined(__FreeBSD__) if (setrlimit(RLIMIT_STACK, &rlim_save) < 0) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Setting stack size failed! (%s)\n", strerror(errno)); } #endif switch_mutex_lock(sth->mutex); switch_thread_cond_signal(sth->cond); switch_mutex_unlock(sth->mutex); switch_core_destroy_memory_pool(&sth->pool); return NULL; } static int switch_system_thread(const char *cmd, switch_bool_t wait) { switch_thread_t *thread; switch_threadattr_t *thd_attr; int ret = 0; struct system_thread_handle *sth; switch_memory_pool_t *pool; if (switch_core_new_memory_pool(&pool) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Pool Failure\n"); return 1; } if (!(sth = switch_core_alloc(pool, sizeof(struct system_thread_handle)))) { switch_core_destroy_memory_pool(&pool); return 1; } sth->pool = pool; sth->cmd = switch_core_strdup(pool, cmd); switch_thread_cond_create(&sth->cond, sth->pool); switch_mutex_init(&sth->mutex, SWITCH_MUTEX_NESTED, sth->pool); switch_mutex_lock(sth->mutex); switch_threadattr_create(&thd_attr, sth->pool); switch_threadattr_stacksize_set(thd_attr, SWITCH_SYSTEM_THREAD_STACKSIZE); switch_threadattr_detach_set(thd_attr, 1); switch_thread_create(&thread, thd_attr, system_thread, sth, sth->pool); if (wait) { switch_thread_cond_wait(sth->cond, sth->mutex); ret = sth->ret; } switch_mutex_unlock(sth->mutex); return ret; } SWITCH_DECLARE(int) switch_max_file_desc(void) { int max = 0; #ifndef WIN32 #if defined(HAVE_GETDTABLESIZE) max = getdtablesize(); #else max = sysconf(_SC_OPEN_MAX); #endif #endif return max; } SWITCH_DECLARE(void) switch_close_extra_files(int *keep, int keep_ttl) { int open_max = switch_max_file_desc(); int i, j; for (i = 3; i < open_max; i++) { if (keep) { for (j = 0; j < keep_ttl; j++) { if (i == keep[j]) { goto skip; } } } close(i); skip: continue; } } #ifdef WIN32 static int switch_system_fork(const char *cmd, switch_bool_t wait) { return switch_system_thread(cmd, wait); } SWITCH_DECLARE(pid_t) switch_fork(void) { return -1; } #else SWITCH_DECLARE(pid_t) switch_fork(void) { int i = fork(); if (!i) { set_low_priority(); } return i; } static int switch_system_fork(const char *cmd, switch_bool_t wait) { int pid; char *dcmd = strdup(cmd); #if defined(HAVE_SETRLIMIT) && !defined(__FreeBSD__) struct rlimit rlim; struct rlimit rlim_save; #endif switch_core_set_signal_handlers(); pid = switch_fork(); if (pid) { if (wait) { waitpid(pid, NULL, 0); } free(dcmd); } else { switch_close_extra_files(NULL, 0); #if defined(HAVE_SETRLIMIT) && !defined(__FreeBSD__) memset(&rlim, 0, sizeof(rlim)); getrlimit(RLIMIT_STACK, &rlim); memset(&rlim_save, 0, sizeof(rlim_save)); getrlimit(RLIMIT_STACK, &rlim_save); rlim.rlim_cur = rlim.rlim_max; if (setrlimit(RLIMIT_STACK, &rlim) < 0) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Setting stack size failed! (%s)\n", strerror(errno)); } #endif if (system(dcmd) == -1) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to execute because of a command error : %s\n", dcmd); } free(dcmd); exit(0); } return 0; } #endif SWITCH_DECLARE(int) switch_system(const char *cmd, switch_bool_t wait) { int (*sys_p)(const char *cmd, switch_bool_t wait); sys_p = switch_test_flag((&runtime), SCF_THREADED_SYSTEM_EXEC) ? switch_system_thread : switch_system_fork; return sys_p(cmd, wait); } SWITCH_DECLARE(int) switch_stream_system_fork(const char *cmd, switch_stream_handle_t *stream) { #ifdef WIN32 return switch_system(cmd, SWITCH_TRUE); #else int fds[2], pid = 0; if (pipe(fds)) { goto end; } else { /* good to go */ pid = switch_fork(); if (pid < 0) { /* ok maybe not */ close(fds[0]); close(fds[1]); goto end; } else if (pid) { /* parent */ char buf[1024] = ""; int bytes; close(fds[1]); while ((bytes = read(fds[0], buf, sizeof(buf))) > 0) { stream->raw_write_function(stream, (unsigned char *)buf, bytes); } close(fds[0]); waitpid(pid, NULL, 0); } else { /* child */ switch_close_extra_files(fds, 2); close(fds[0]); dup2(fds[1], STDOUT_FILENO); switch_system(cmd, SWITCH_TRUE); close(fds[1]); exit(0); } } end: return 0; #endif } SWITCH_DECLARE(switch_status_t) switch_core_get_stacksizes(switch_size_t *cur, switch_size_t *max) { #ifdef HAVE_SETRLIMIT struct rlimit rlp; memset(&rlp, 0, sizeof(rlp)); getrlimit(RLIMIT_STACK, &rlp); *cur = rlp.rlim_cur; *max = rlp.rlim_max; return SWITCH_STATUS_SUCCESS; #else return SWITCH_STATUS_FALSE; #endif } SWITCH_DECLARE(int) switch_stream_system(const char *cmd, switch_stream_handle_t *stream) { #ifdef WIN32 stream->write_function(stream, "Capturing output not supported.\n"); return switch_system(cmd, SWITCH_TRUE); #else return switch_stream_system_fork(cmd, stream); #endif } SWITCH_DECLARE(uint16_t) switch_core_get_rtp_port_range_start_port() { uint16_t start_port = 0; /* By default pass rtp port range start value as zero in order to get actual * RTP port range start value as configured */ start_port = (uint16_t)switch_rtp_set_start_port((switch_port_t)start_port); return start_port; } SWITCH_DECLARE(uint16_t) switch_core_get_rtp_port_range_end_port() { uint16_t end_port = 0; /* By default pass rtp port range end value as zero in order to get actual * RTP port range end value as configured */ end_port = (uint16_t)switch_rtp_set_end_port((switch_port_t)end_port); return end_port; } /* For Emacs: * Local Variables: * mode:c * indent-tabs-mode:t * tab-width:4 * c-basic-offset:4 * End: * For VIM: * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet: */ [-- Attachment #6 --] /* * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application * Copyright (C) 2005-2014, Anthony Minessale II <anthm@freeswitch.org> * * Version: MPL 1.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application * * The Initial Developer of the Original Code is * Anthony Minessale II <anthm@freeswitch.org> * Portions created by the Initial Developer are Copyright (C) * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Anthony Minessale II <anthm@freeswitch.org> * Michael Jerris <mike@jerris.com> * Pawel Pierscionek <pawel@voiceworks.pl> * Bret McDanel <trixter AT 0xdecafbad.com> * * * switch.c -- Main * */ #ifndef _XOPEN_SOURCE #define _XOPEN_SOURCE 600 #endif #ifndef WIN32 #include <poll.h> #ifdef HAVE_SETRLIMIT #include <sys/resource.h> #endif #endif #ifdef __linux__ #include <sys/prctl.h> #endif #include <switch.h> #include "private/switch_core_pvt.h" /* pid filename: Stores the process id of the freeswitch process */ #define PIDFILE "freeswitch.pid" static char *pfile = PIDFILE; static int system_ready = 0; /* Picky compiler */ #ifdef __ICC #pragma warning (disable:167) #endif #ifdef WIN32 /* If we are a windows service, what should we be called */ #define SERVICENAME_DEFAULT "FreeSWITCH" #define SERVICENAME_MAXLEN 256 static char service_name[SERVICENAME_MAXLEN]; static switch_core_flag_t service_flags = SCF_NONE; #include <winsock2.h> #include <windows.h> /* event to signal shutdown (for you unix people, this is like a pthread_cond) */ static HANDLE shutdown_event; #ifndef PATH_MAX #define PATH_MAX 256 #endif #endif /* signal handler for when freeswitch is running in background mode. * signal triggers the shutdown of freeswitch # */ static void handle_SIGILL(int sig) { int32_t arg = 0; if (sig) {}; /* send shutdown signal to the freeswitch core */ switch_core_session_ctl(SCSC_SHUTDOWN, &arg); return; } /* kill a freeswitch process running in background mode */ static int freeswitch_kill_background() { FILE *f; /* FILE handle to open the pid file */ char path[PATH_MAX] = ""; /* full path of the PID file */ pid_t pid = 0; /* pid from the pid file */ /* set the globals so we can use the global paths. */ switch_core_set_globals(); /* get the full path of the pid file. */ switch_snprintf(path, sizeof(path), "%s%s%s", SWITCH_GLOBAL_dirs.run_dir, SWITCH_PATH_SEPARATOR, pfile); /* open the pid file */ if ((f = fopen(path, "r")) == 0) { /* pid file does not exist */ fprintf(stderr, "Cannot open pid file %s.\n", path); return 255; } /* pull the pid from the file */ if (fscanf(f, "%d", (int *) (intptr_t) & pid) != 1) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unable to get the pid!\n"); } /* if we have a valid pid */ if (pid > 0) { /* kill the freeswitch running at the pid we found */ fprintf(stderr, "Killing: %d\n", (int) pid); #ifdef WIN32 /* for windows we need the event to signal for shutting down a background FreeSWITCH */ snprintf(path, sizeof(path), "Global\\Freeswitch.%d", pid); /* open the event so we can signal it */ shutdown_event = OpenEvent(EVENT_MODIFY_STATE, FALSE, path); /* did we successfully open the event */ if (!shutdown_event) { /* we can't get the event, so we can't signal the process to shutdown */ fprintf(stderr, "ERROR: Can't Shutdown: %d\n", (int) pid); } else { /* signal the event to shutdown */ SetEvent(shutdown_event); /* cleanup */ CloseHandle(shutdown_event); } #else /* for unix, send the signal to kill. */ kill(pid, SIGTERM); #endif } /* be nice and close the file handle to the pid file */ fclose(f); return 0; } #ifdef WIN32 /* we need these vars to handle the service */ SERVICE_STATUS_HANDLE hStatus; SERVICE_STATUS status; /* Handler function for service start/stop from the service */ void WINAPI ServiceCtrlHandler(DWORD control) { switch (control) { case SERVICE_CONTROL_SHUTDOWN: case SERVICE_CONTROL_STOP: /* Shutdown freeswitch */ switch_core_destroy(); /* set service status values */ status.dwCurrentState = SERVICE_STOPPED; status.dwWin32ExitCode = 0; status.dwCheckPoint = 0; status.dwWaitHint = 0; break; case SERVICE_CONTROL_INTERROGATE: /* we already set the service status every time it changes. */ /* if there are other times we change it and don't update, we should do so here */ break; } SetServiceStatus(hStatus, &status); } /* the main service entry point */ void WINAPI service_main(DWORD numArgs, char **args) { switch_core_flag_t flags = SCF_USE_SQL | SCF_USE_AUTO_NAT | SCF_USE_NAT_MAPPING | SCF_CALIBRATE_CLOCK | SCF_USE_CLOCK_RT; const char *err = NULL; /* error value for return from freeswitch initialization */ /* Override flags if they have been set earlier */ if (service_flags != SCF_NONE) flags = service_flags; /* we have to initialize the service-specific stuff */ memset(&status, 0, sizeof(SERVICE_STATUS)); status.dwServiceType = SERVICE_WIN32; status.dwCurrentState = SERVICE_START_PENDING; status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN; /* register our handler for service control messages */ hStatus = RegisterServiceCtrlHandler(service_name, &ServiceCtrlHandler); /* update the service status */ SetServiceStatus(hStatus, &status); switch_core_set_globals(); /* attempt to initialize freeswitch and load modules */ if (switch_core_init_and_modload(flags, SWITCH_FALSE, &err) != SWITCH_STATUS_SUCCESS) { /* freeswitch did not start successfully */ status.dwCurrentState = SERVICE_STOPPED; } else { /* freeswitch started */ status.dwCurrentState = SERVICE_RUNNING; } /* update the service status */ SetServiceStatus(hStatus, &status); } #else static int check_fd(int fd, int ms) { struct pollfd pfds[2] = { { 0 } }; int s, r = 0, i = 0; pfds[0].fd = fd; pfds[0].events = POLLIN | POLLERR; s = poll(pfds, 1, ms); if (s == 0 || s == -1) { r = s; } else { r = -1; if ((pfds[0].revents & POLLIN)) { if ((i = read(fd, &r, sizeof(r))) > -1) { (void)write(fd, &r, sizeof(r)); } } } return r; } static void daemonize(int *fds) { int fd; pid_t pid; unsigned int sanity = 60; if (!fds) { switch (fork()) { case 0: /* child process */ break; case -1: fprintf(stderr, "Error Backgrounding (fork)! %d - %s\n", errno, strerror(errno)); exit(EXIT_SUCCESS); break; default: /* parent process */ exit(EXIT_SUCCESS); } if (setsid() < 0) { fprintf(stderr, "Error Backgrounding (setsid)! %d - %s\n", errno, strerror(errno)); exit(EXIT_SUCCESS); } } pid = switch_fork(); switch (pid) { case 0: /* child process */ if (fds) { close(fds[0]); } break; case -1: fprintf(stderr, "Error Backgrounding (fork2)! %d - %s\n", errno, strerror(errno)); exit(EXIT_SUCCESS); break; default: /* parent process */ fprintf(stderr, "%d Backgrounding.\n", (int) pid); if (fds) { char *o; close(fds[1]); if ((o = getenv("FREESWITCH_BG_TIMEOUT"))) { int tmp = atoi(o); if (tmp > 0) { sanity = tmp; } } do { system_ready = check_fd(fds[0], 2000); if (system_ready == 0) { printf("FreeSWITCH[%d] Waiting for background process pid:%d to be ready.....\n", (int)getpid(), (int) pid); } } while (--sanity && system_ready == 0); shutdown(fds[0], 2); close(fds[0]); fds[0] = -1; if (system_ready < 0) { printf("FreeSWITCH[%d] Error starting system! pid:%d\n", (int)getpid(), (int) pid); kill(pid, 9); exit(EXIT_FAILURE); } printf("FreeSWITCH[%d] System Ready pid:%d\n", (int) getpid(), (int) pid); } exit(EXIT_SUCCESS); } if (fds) { setsid(); } /* redirect std* to null */ fd = open("/dev/null", O_RDONLY); switch_assert( fd >= 0 ); if (fd != 0) { dup2(fd, 0); close(fd); } fd = open("/dev/null", O_WRONLY); switch_assert( fd >= 0 ); if (fd != 1) { dup2(fd, 1); close(fd); } fd = open("/dev/null", O_WRONLY); switch_assert( fd >= 0 ); if (fd != 2) { dup2(fd, 2); close(fd); } return; } static pid_t reincarnate_child = 0; static void reincarnate_handle_sigterm (int sig) { if (!sig) return; if (reincarnate_child) kill(reincarnate_child, sig); return; } static void reincarnate_protect(char **argv) { int i; struct sigaction sa, sa_dfl, sa4_prev, sa15_prev, sa17_prev; memset(&sa, 0, sizeof(sa)); memset(&sa_dfl, 0, sizeof(sa_dfl)); sa.sa_handler = reincarnate_handle_sigterm; sa_dfl.sa_handler = SIG_DFL; refork: if ((i=fork())) { /* parent */ int s; pid_t r; reincarnate_child = i; sigaction(SIGILL, &sa, &sa4_prev); sigaction(SIGTERM, &sa, &sa15_prev); sigaction(SIGCHLD, &sa_dfl, &sa17_prev); rewait: r = waitpid(i, &s, 0); if (r == (pid_t)-1) { if (errno == EINTR) goto rewait; exit(EXIT_FAILURE); } if (r != i) goto rewait; if (WIFEXITED(s) && (WEXITSTATUS(s) == EXIT_SUCCESS || WEXITSTATUS(s) == EXIT_FAILURE)) { exit(WEXITSTATUS(s)); } if (WIFEXITED(s) || WIFSIGNALED(s)) { sigaction(SIGILL, &sa4_prev, NULL); sigaction(SIGTERM, &sa15_prev, NULL); sigaction(SIGCHLD, &sa17_prev, NULL); if (argv) { if (execv(argv[0], argv) == -1) { char buf[256]; fprintf(stderr, "Reincarnate execv() failed: %d %s\n", errno, switch_strerror_r(errno, buf, sizeof(buf))); } fprintf(stderr, "Trying reincarnate-reexec plan B...\n"); if (execvp(argv[0], argv) == -1) { char buf[256]; fprintf(stderr, "Reincarnate execvp() failed: %d %s\n", errno, switch_strerror_r(errno, buf, sizeof(buf))); } fprintf(stderr, "Falling back to normal reincarnate behavior...\n"); goto refork; } else goto refork; } goto rewait; } else { /* child */ #ifdef __linux__ prctl(PR_SET_PDEATHSIG, SIGTERM); #endif } } #endif static const char usage[] = "Usage: freeswitch [OPTIONS]\n\n" "These are the optional arguments you can pass to freeswitch:\n" #ifdef WIN32 "\t-service [name] -- start freeswitch as a service, cannot be used if loaded as a console app\n" "\t-install [name] -- install freeswitch as a service, with optional service name\n" "\t-uninstall -- remove freeswitch as a service\n" "\t-monotonic-clock -- use monotonic clock as timer source\n" #else "\t-nf -- no forking\n" "\t-reincarnate -- restart the switch on an uncontrolled exit\n" "\t-reincarnate-reexec -- run execv on a restart (helpful for upgrades)\n" "\t-u [user] -- specify user to switch to\n" "\t-g [group] -- specify group to switch to\n" #endif #ifdef HAVE_SETRLIMIT #ifndef FS_64BIT "\t-waste -- allow memory waste\n" #endif "\t-core -- dump cores\n" #endif "\t-help -- this message\n" "\t-version -- print the version and exit\n" "\t-rp -- enable high(realtime) priority settings\n" "\t-lp -- enable low priority settings\n" "\t-np -- enable normal priority settings\n" "\t-vg -- run under valgrind\n" "\t-nosql -- disable internal sql scoreboard\n" "\t-heavy-timer -- Heavy Timer, possibly more accurate but at a cost\n" "\t-nonat -- disable auto nat detection\n" "\t-nonatmap -- disable auto nat port mapping\n" "\t-nocal -- disable clock calibration\n" "\t-nort -- disable clock clock_realtime\n" "\t-stop -- stop freeswitch\n" "\t-nc -- do not output to a console and background\n" #ifndef WIN32 "\t-ncwait -- do not output to a console and background but wait until the system is ready before exiting (implies -nc)\n" #endif "\t-c -- output to a console and stay in the foreground\n" "\n\tOptions to control locations of files:\n" "\t-base [basedir] -- alternate prefix directory\n" "\t-cfgname [filename] -- alternate filename for FreeSWITCH main configuration file\n" "\t-conf [confdir] -- alternate directory for FreeSWITCH configuration files\n" "\t-log [logdir] -- alternate directory for logfiles\n" "\t-run [rundir] -- alternate directory for runtime files\n" "\t-db [dbdir] -- alternate directory for the internal database\n" "\t-mod [moddir] -- alternate directory for modules\n" "\t-htdocs [htdocsdir] -- alternate directory for htdocs\n" "\t-scripts [scriptsdir] -- alternate directory for scripts\n" "\t-temp [directory] -- alternate directory for temporary files\n" "\t-grammar [directory] -- alternate directory for grammar files\n" "\t-certs [directory] -- alternate directory for certificates\n" "\t-recordings [directory] -- alternate directory for recordings\n" "\t-storage [directory] -- alternate directory for voicemail storage\n" "\t-cache [directory] -- alternate directory for cache files\n" "\t-sounds [directory] -- alternate directory for sound files\n"; /** * Check if value string starts with "-" */ static switch_bool_t is_option(const char *p) { /* skip whitespaces */ while ((*p == 13) || (*p == 10) || (*p == 9) || (*p == 32) || (*p == 11)) p++; return (p[0] == '-'); } /* the main application entry point */ int main(int argc, char *argv[]) { char pid_path[PATH_MAX] = ""; /* full path to the pid file */ char pid_buffer[32] = ""; /* pid string */ char old_pid_buffer[32] = { 0 }; /* pid string */ switch_size_t pid_len, old_pid_len; const char *err = NULL; /* error value for return from freeswitch initialization */ #ifndef WIN32 switch_bool_t nf = SWITCH_FALSE; /* TRUE if we are running in nofork mode */ switch_bool_t do_wait = SWITCH_FALSE; char *runas_user = NULL; char *runas_group = NULL; switch_bool_t reincarnate = SWITCH_FALSE, reincarnate_reexec = SWITCH_FALSE; int fds[2] = { 0, 0 }; #else const switch_bool_t nf = SWITCH_TRUE; /* On Windows, force nf to true*/ switch_bool_t win32_service = SWITCH_FALSE; #endif switch_bool_t nc = SWITCH_FALSE; /* TRUE if we are running in noconsole mode */ pid_t pid = 0; int i, x; char *opts; char opts_str[1024] = ""; char *local_argv[1024] = { 0 }; int local_argc = argc; char *arg_argv[128] = { 0 }; int alt_dirs = 0, alt_base = 0, log_set = 0, run_set = 0, do_kill = 0; int priority = 0; #if (defined(__SVR4) && defined(__sun)) switch_core_flag_t flags = SCF_USE_SQL | SCF_CALIBRATE_CLOCK | SCF_USE_CLOCK_RT; #else switch_core_flag_t flags = SCF_USE_SQL | SCF_USE_AUTO_NAT | SCF_USE_NAT_MAPPING | SCF_CALIBRATE_CLOCK | SCF_USE_CLOCK_RT; #endif int ret = 0; switch_status_t destroy_status; switch_file_t *fd; switch_memory_pool_t *pool = NULL; #ifdef HAVE_SETRLIMIT #ifndef FS_64BIT switch_bool_t waste = SWITCH_FALSE; #endif #endif for (x = 0; x < argc; x++) { local_argv[x] = argv[x]; } if ((opts = getenv("FREESWITCH_OPTS"))) { strncpy(opts_str, opts, sizeof(opts_str) - 1); i = switch_separate_string(opts_str, ' ', arg_argv, (sizeof(arg_argv) / sizeof(arg_argv[0]))); for (x = 0; x < i; x++) { local_argv[local_argc++] = arg_argv[x]; } } if (local_argv[0] && strstr(local_argv[0], "freeswitchd")) { nc = SWITCH_TRUE; } for (x = 1; x < local_argc; x++) { if (switch_strlen_zero(local_argv[x])) continue; if (!strcmp(local_argv[x], "-help") || !strcmp(local_argv[x], "-h") || !strcmp(local_argv[x], "-?")) { printf("%s\n", usage); exit(EXIT_SUCCESS); } #ifdef WIN32 if (x == 1 && !strcmp(local_argv[x], "-service")) { /* New installs will always have the service name specified, but keep a default for compat */ x++; if (!switch_strlen_zero(local_argv[x])) { switch_copy_string(service_name, local_argv[x], SERVICENAME_MAXLEN); } else { switch_copy_string(service_name, SERVICENAME_DEFAULT, SERVICENAME_MAXLEN); } win32_service = SWITCH_TRUE; continue; } else if (x == 1 && !strcmp(local_argv[x], "-install")) { char servicePath[PATH_MAX]; char exePath[PATH_MAX]; SC_HANDLE hService; SC_HANDLE hSCManager; SERVICE_DESCRIPTION desc; desc.lpDescription = "The FreeSWITCH service."; x++; if (!switch_strlen_zero(local_argv[x])) { switch_copy_string(service_name, local_argv[x], SERVICENAME_MAXLEN); } else { switch_copy_string(service_name, SERVICENAME_DEFAULT, SERVICENAME_MAXLEN); } GetModuleFileName(NULL, exePath, sizeof(exePath)); snprintf(servicePath, sizeof(servicePath), "%s -service %s", exePath, service_name); /* Perform service installation */ hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); if (!hSCManager) { fprintf(stderr, "Could not open service manager (%u).\n", GetLastError()); exit(EXIT_FAILURE); } hService = CreateService(hSCManager, service_name, service_name, GENERIC_READ | GENERIC_EXECUTE | SERVICE_CHANGE_CONFIG, SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_IGNORE, servicePath, NULL, NULL, NULL, NULL, /* Service start name */ NULL); if (!hService) { fprintf(stderr, "Error creating freeswitch service (%u).\n", GetLastError()); CloseServiceHandle(hSCManager); exit(EXIT_FAILURE); } /* Set desc, and don't care if it succeeds */ if (!ChangeServiceConfig2(hService, SERVICE_CONFIG_DESCRIPTION, &desc)) { fprintf(stderr, "FreeSWITCH installed, but could not set the service description (%u).\n", GetLastError()); } CloseServiceHandle(hService); CloseServiceHandle(hSCManager); exit(EXIT_SUCCESS); } else if (x == 1 && !strcmp(local_argv[x], "-uninstall")) { SC_HANDLE hService; SC_HANDLE hSCManager; BOOL deleted; x++; if (!switch_strlen_zero(local_argv[x])) { switch_copy_string(service_name, local_argv[x], SERVICENAME_MAXLEN); } else { switch_copy_string(service_name, SERVICENAME_DEFAULT, SERVICENAME_MAXLEN); } /* Do the uninstallation */ hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); if (!hSCManager) { fprintf(stderr, "Could not open service manager (%u).\n", GetLastError()); exit(EXIT_FAILURE); } hService = OpenService(hSCManager, service_name, DELETE); if (!hService) { fprintf(stderr, "Error opening service (%u).\n", GetLastError()); CloseServiceHandle(hSCManager); exit(EXIT_FAILURE); } /* remove the service! */ deleted = DeleteService(hService); if (!deleted) { fprintf(stderr, "Error deleting service (%u).\n", GetLastError()); } CloseServiceHandle(hService); CloseServiceHandle(hSCManager); exit(deleted ? EXIT_SUCCESS : EXIT_FAILURE); } else if (!strcmp(local_argv[x], "-monotonic-clock")) { flags |= SCF_USE_WIN32_MONOTONIC; } #else else if (!strcmp(local_argv[x], "-u")) { x++; if (switch_strlen_zero(local_argv[x]) || is_option(local_argv[x])) { fprintf(stderr, "Option '%s' requires an argument!\n", local_argv[x - 1]); exit(EXIT_FAILURE); } runas_user = local_argv[x]; } else if (!strcmp(local_argv[x], "-g")) { x++; if (switch_strlen_zero(local_argv[x]) || is_option(local_argv[x])) { fprintf(stderr, "Option '%s' requires an argument!\n", local_argv[x - 1]); exit(EXIT_FAILURE); } runas_group = local_argv[x]; } else if (!strcmp(local_argv[x], "-nf")) { nf = SWITCH_TRUE; } else if (!strcmp(local_argv[x], "-reincarnate")) { reincarnate = SWITCH_TRUE; } else if (!strcmp(local_argv[x], "-reincarnate-reexec")) { reincarnate = SWITCH_TRUE; reincarnate_reexec = SWITCH_TRUE; } else if (!strcmp(local_argv[x], "-version")) { fprintf(stdout, "FreeSWITCH version: %s (%s)\n", switch_version_full(), switch_version_revision_human()); exit(EXIT_SUCCESS); } #endif #ifdef HAVE_SETRLIMIT else if (!strcmp(local_argv[x], "-core")) { struct rlimit rlp; memset(&rlp, 0, sizeof(rlp)); rlp.rlim_cur = RLIM_INFINITY; rlp.rlim_max = RLIM_INFINITY; setrlimit(RLIMIT_CORE, &rlp); } else if (!strcmp(local_argv[x], "-waste")) { #ifndef FS_64BIT fprintf(stderr, "WARNING: Wasting up to 8 megs of memory per thread.\n"); sleep(2); waste = SWITCH_TRUE; #endif } else if (!strcmp(local_argv[x], "-no-auto-stack")) { #ifndef FS_64BIT waste = SWITCH_TRUE; #endif } #endif else if (!strcmp(local_argv[x], "-hp") || !strcmp(local_argv[x], "-rp")) { priority = 2; } else if (!strcmp(local_argv[x], "-lp")) { priority = -1; } else if (!strcmp(local_argv[x], "-np")) { priority = 1; } else if (!strcmp(local_argv[x], "-nosql")) { flags &= ~SCF_USE_SQL; } else if (!strcmp(local_argv[x], "-nonat")) { flags &= ~SCF_USE_AUTO_NAT; } else if (!strcmp(local_argv[x], "-nonatmap")) { flags &= ~SCF_USE_NAT_MAPPING; } else if (!strcmp(local_argv[x], "-heavy-timer")) { flags |= SCF_USE_HEAVY_TIMING; } else if (!strcmp(local_argv[x], "-nort")) { flags &= ~SCF_USE_CLOCK_RT; } else if (!strcmp(local_argv[x], "-nocal")) { flags &= ~SCF_CALIBRATE_CLOCK; } else if (!strcmp(local_argv[x], "-vg")) { flags |= SCF_VG; } else if (!strcmp(local_argv[x], "-stop")) { do_kill = SWITCH_TRUE; } else if (!strcmp(local_argv[x], "-nc")) { nc = SWITCH_TRUE; } #ifndef WIN32 else if (!strcmp(local_argv[x], "-ncwait")) { nc = SWITCH_TRUE; do_wait = SWITCH_TRUE; } #endif else if (!strcmp(local_argv[x], "-c")) { nc = SWITCH_FALSE; } else if (!strcmp(local_argv[x], "-conf")) { x++; if (switch_strlen_zero(local_argv[x]) || is_option(local_argv[x])) { fprintf(stderr, "When using -conf you must specify a config directory\n"); return 255; } SWITCH_GLOBAL_dirs.conf_dir = (char *) malloc(strlen(local_argv[x]) + 1); if (!SWITCH_GLOBAL_dirs.conf_dir) { fprintf(stderr, "Allocation error\n"); return 255; } strcpy(SWITCH_GLOBAL_dirs.conf_dir, local_argv[x]); alt_dirs++; } else if (!strcmp(local_argv[x], "-mod")) { x++; if (switch_strlen_zero(local_argv[x]) || is_option(local_argv[x])) { fprintf(stderr, "When using -mod you must specify a module directory\n"); return 255; } SWITCH_GLOBAL_dirs.mod_dir = (char *) malloc(strlen(local_argv[x]) + 1); if (!SWITCH_GLOBAL_dirs.mod_dir) { fprintf(stderr, "Allocation error\n"); return 255; } strcpy(SWITCH_GLOBAL_dirs.mod_dir, local_argv[x]); } else if (!strcmp(local_argv[x], "-log")) { x++; if (switch_strlen_zero(local_argv[x]) || is_option(local_argv[x])) { fprintf(stderr, "When using -log you must specify a log directory\n"); return 255; } SWITCH_GLOBAL_dirs.log_dir = (char *) malloc(strlen(local_argv[x]) + 1); if (!SWITCH_GLOBAL_dirs.log_dir) { fprintf(stderr, "Allocation error\n"); return 255; } strcpy(SWITCH_GLOBAL_dirs.log_dir, local_argv[x]); alt_dirs++; log_set = SWITCH_TRUE; } else if (!strcmp(local_argv[x], "-run")) { x++; if (switch_strlen_zero(local_argv[x]) || is_option(local_argv[x])) { fprintf(stderr, "When using -run you must specify a pid directory\n"); return 255; } SWITCH_GLOBAL_dirs.run_dir = (char *) malloc(strlen(local_argv[x]) + 1); if (!SWITCH_GLOBAL_dirs.run_dir) { fprintf(stderr, "Allocation error\n"); return 255; } strcpy(SWITCH_GLOBAL_dirs.run_dir, local_argv[x]); run_set = SWITCH_TRUE; } else if (!strcmp(local_argv[x], "-db")) { x++; if (switch_strlen_zero(local_argv[x]) || is_option(local_argv[x])) { fprintf(stderr, "When using -db you must specify a db directory\n"); return 255; } SWITCH_GLOBAL_dirs.db_dir = (char *) malloc(strlen(local_argv[x]) + 1); if (!SWITCH_GLOBAL_dirs.db_dir) { fprintf(stderr, "Allocation error\n"); return 255; } strcpy(SWITCH_GLOBAL_dirs.db_dir, local_argv[x]); alt_dirs++; } else if (!strcmp(local_argv[x], "-scripts")) { x++; if (switch_strlen_zero(local_argv[x]) || is_option(local_argv[x])) { fprintf(stderr, "When using -scripts you must specify a scripts directory\n"); return 255; } SWITCH_GLOBAL_dirs.script_dir = (char *) malloc(strlen(local_argv[x]) + 1); if (!SWITCH_GLOBAL_dirs.script_dir) { fprintf(stderr, "Allocation error\n"); return 255; } strcpy(SWITCH_GLOBAL_dirs.script_dir, local_argv[x]); } else if (!strcmp(local_argv[x], "-htdocs")) { x++; if (switch_strlen_zero(local_argv[x]) || is_option(local_argv[x])) { fprintf(stderr, "When using -htdocs you must specify a htdocs directory\n"); return 255; } SWITCH_GLOBAL_dirs.htdocs_dir = (char *) malloc(strlen(local_argv[x]) + 1); if (!SWITCH_GLOBAL_dirs.htdocs_dir) { fprintf(stderr, "Allocation error\n"); return 255; } strcpy(SWITCH_GLOBAL_dirs.htdocs_dir, local_argv[x]); } else if (!strcmp(local_argv[x], "-base")) { x++; if (switch_strlen_zero(local_argv[x]) || is_option(local_argv[x])) { fprintf(stderr, "When using -base you must specify a base directory\n"); return 255; } SWITCH_GLOBAL_dirs.base_dir = (char *) malloc(strlen(local_argv[x]) + 1); if (!SWITCH_GLOBAL_dirs.base_dir) { fprintf(stderr, "Allocation error\n"); return 255; } strcpy(SWITCH_GLOBAL_dirs.base_dir, local_argv[x]); alt_base = 1; } else if (!strcmp(local_argv[x], "-temp")) { x++; if (switch_strlen_zero(local_argv[x]) || is_option(local_argv[x])) { fprintf(stderr, "When using -temp you must specify a temp directory\n"); return 255; } SWITCH_GLOBAL_dirs.temp_dir = (char *) malloc(strlen(local_argv[x]) + 1); if (!SWITCH_GLOBAL_dirs.temp_dir) { fprintf(stderr, "Allocation error\n"); return 255; } strcpy(SWITCH_GLOBAL_dirs.temp_dir, local_argv[x]); } else if (!strcmp(local_argv[x], "-storage")) { x++; if (switch_strlen_zero(local_argv[x]) || is_option(local_argv[x])) { fprintf(stderr, "When using -storage you must specify a storage directory\n"); return 255; } SWITCH_GLOBAL_dirs.storage_dir = (char *) malloc(strlen(local_argv[x]) + 1); if (!SWITCH_GLOBAL_dirs.storage_dir) { fprintf(stderr, "Allocation error\n"); return 255; } strcpy(SWITCH_GLOBAL_dirs.storage_dir, local_argv[x]); } else if (!strcmp(local_argv[x], "-cache")) { x++; if (switch_strlen_zero(local_argv[x]) || is_option(local_argv[x])) { fprintf(stderr, "When using -cache you must specify a cache directory\n"); return 255; } SWITCH_GLOBAL_dirs.cache_dir = (char *) malloc(strlen(local_argv[x]) + 1); if (!SWITCH_GLOBAL_dirs.cache_dir) { fprintf(stderr, "Allocation error\n"); return 255; } strcpy(SWITCH_GLOBAL_dirs.cache_dir, local_argv[x]); } else if (!strcmp(local_argv[x], "-recordings")) { x++; if (switch_strlen_zero(local_argv[x]) || is_option(local_argv[x])) { fprintf(stderr, "When using -recordings you must specify a recording directory\n"); return 255; } SWITCH_GLOBAL_dirs.recordings_dir = (char *) malloc(strlen(local_argv[x]) + 1); if (!SWITCH_GLOBAL_dirs.recordings_dir) { fprintf(stderr, "Allocation error\n"); return 255; } strcpy(SWITCH_GLOBAL_dirs.recordings_dir, local_argv[x]); } else if (!strcmp(local_argv[x], "-grammar")) { x++; if (switch_strlen_zero(local_argv[x]) || is_option(local_argv[x])) { fprintf(stderr, "When using -grammar you must specify a grammar directory\n"); return 255; } SWITCH_GLOBAL_dirs.grammar_dir = (char *) malloc(strlen(local_argv[x]) + 1); if (!SWITCH_GLOBAL_dirs.grammar_dir) { fprintf(stderr, "Allocation error\n"); return 255; } strcpy(SWITCH_GLOBAL_dirs.grammar_dir, local_argv[x]); } else if (!strcmp(local_argv[x], "-certs")) { x++; if (switch_strlen_zero(local_argv[x]) || is_option(local_argv[x])) { fprintf(stderr, "When using -certs you must specify a certificates directory\n"); return 255; } SWITCH_GLOBAL_dirs.certs_dir = (char *) malloc(strlen(local_argv[x]) + 1); if (!SWITCH_GLOBAL_dirs.certs_dir) { fprintf(stderr, "Allocation error\n"); return 255; } strcpy(SWITCH_GLOBAL_dirs.certs_dir, local_argv[x]); } else if (!strcmp(local_argv[x], "-sounds")) { x++; if (switch_strlen_zero(local_argv[x]) || is_option(local_argv[x])) { fprintf(stderr, "When using -sounds you must specify a sounds directory\n"); return 255; } SWITCH_GLOBAL_dirs.sounds_dir = (char *) malloc(strlen(local_argv[x]) + 1); if (!SWITCH_GLOBAL_dirs.sounds_dir) { fprintf(stderr, "Allocation error\n"); return 255; } strcpy(SWITCH_GLOBAL_dirs.sounds_dir, local_argv[x]); } else if (!strcmp(local_argv[x], "-cfgname")) { x++; if (switch_strlen_zero(local_argv[x]) || is_option(local_argv[x])) { fprintf(stderr, "When using -cfgname you must specify a filename\n"); return 255; } SWITCH_GLOBAL_filenames.conf_name = (char *) malloc(strlen(local_argv[x]) + 1); if (!SWITCH_GLOBAL_filenames.conf_name) { fprintf(stderr, "Allocation error\n"); return 255; } strcpy(SWITCH_GLOBAL_filenames.conf_name, local_argv[x]); } /* Unknown option (always last!) */ else { fprintf(stderr, "Unknown option '%s', see '%s -help' for a list of valid options\n", local_argv[x], local_argv[0]); exit(EXIT_FAILURE); } } if (log_set && !run_set) { SWITCH_GLOBAL_dirs.run_dir = (char *) malloc(strlen(SWITCH_GLOBAL_dirs.log_dir) + 1); if (!SWITCH_GLOBAL_dirs.run_dir) { fprintf(stderr, "Allocation error\n"); return 255; } strcpy(SWITCH_GLOBAL_dirs.run_dir, SWITCH_GLOBAL_dirs.log_dir); } if (do_kill) { return freeswitch_kill_background(); } if (apr_initialize() != SWITCH_STATUS_SUCCESS) { fprintf(stderr, "FATAL ERROR! Could not initialize APR\n"); return 255; } if (alt_dirs && alt_dirs != 3 && !alt_base) { fprintf(stderr, "You must specify all or none of -conf, -log, and -db\n"); return 255; } #ifndef FS_64BIT #if defined(HAVE_SETRLIMIT) && !defined(__sun) if (!waste && !(flags & SCF_VG)) { struct rlimit rlp; memset(&rlp, 0, sizeof(rlp)); getrlimit(RLIMIT_STACK, &rlp); if (rlp.rlim_cur != SWITCH_THREAD_STACKSIZE) { char buf[1024] = ""; int i = 0; memset(&rlp, 0, sizeof(rlp)); rlp.rlim_cur = SWITCH_THREAD_STACKSIZE; rlp.rlim_max = SWITCH_SYSTEM_THREAD_STACKSIZE; setrlimit(RLIMIT_STACK, &rlp); apr_terminate(); if (argv) ret = (int) execv(argv[0], argv); for (i = 0; i < argc; i++) { switch_snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "%s ", argv[i]); } return system(buf); } } #endif #endif signal(SIGILL, handle_SIGILL); signal(SIGTERM, handle_SIGILL); #ifndef WIN32 if (do_wait) { if (pipe(fds)) { fprintf(stderr, "System Error!\n"); exit(-1); } } #endif if (nc) { #ifdef WIN32 FreeConsole(); #else if (!nf) { daemonize(do_wait ? fds : NULL); } #endif } #ifndef WIN32 if (reincarnate) reincarnate_protect(reincarnate_reexec ? argv : NULL); #endif if (switch_core_set_process_privileges() < 0) { return 255; } switch (priority) { case 2: set_realtime_priority(); break; case 1: set_normal_priority(); break; case -1: set_low_priority(); break; default: set_auto_priority(); break; } switch_core_setrlimits(); #ifndef WIN32 if (runas_user || runas_group) { if (change_user_group(runas_user, runas_group) < 0) { fprintf(stderr, "Failed to switch user [%s] / group [%s]\n", switch_strlen_zero(runas_user) ? "-" : runas_user, switch_strlen_zero(runas_group) ? "-" : runas_group); return 255; } } #else if (win32_service) { /* Attempt to start service */ SERVICE_TABLE_ENTRY dispatchTable[] = { {service_name, &service_main} , {NULL, NULL} }; service_flags = flags; /* copy parsed flags for service startup */ if (StartServiceCtrlDispatcher(dispatchTable) == 0) { /* Not loaded as a service */ fprintf(stderr, "Error Freeswitch loaded as a console app with -service option\n"); fprintf(stderr, "To install the service load freeswitch with -install\n"); } exit(EXIT_SUCCESS); } #endif switch_core_set_globals(); pid = getpid(); memset(pid_buffer, 0, sizeof(pid_buffer)); switch_snprintf(pid_path, sizeof(pid_path), "%s%s%s", SWITCH_GLOBAL_dirs.run_dir, SWITCH_PATH_SEPARATOR, pfile); switch_snprintf(pid_buffer, sizeof(pid_buffer), "%d", pid); pid_len = strlen(pid_buffer); apr_pool_create(&pool, NULL); switch_dir_make_recursive(SWITCH_GLOBAL_dirs.run_dir, SWITCH_DEFAULT_DIR_PERMS, pool); if (switch_file_open(&fd, pid_path, SWITCH_FOPEN_READ, SWITCH_FPROT_UREAD | SWITCH_FPROT_UWRITE, pool) == SWITCH_STATUS_SUCCESS) { old_pid_len = sizeof(old_pid_buffer) -1; switch_file_read(fd, old_pid_buffer, &old_pid_len); switch_file_close(fd); } if (switch_file_open(&fd, pid_path, SWITCH_FOPEN_WRITE | SWITCH_FOPEN_CREATE | SWITCH_FOPEN_TRUNCATE, SWITCH_FPROT_UREAD | SWITCH_FPROT_UWRITE, pool) != SWITCH_STATUS_SUCCESS) { fprintf(stderr, "Cannot open pid file %s.\n", pid_path); return 255; } if (switch_file_lock(fd, SWITCH_FLOCK_EXCLUSIVE | SWITCH_FLOCK_NONBLOCK) != SWITCH_STATUS_SUCCESS) { fprintf(stderr, "Cannot lock pid file %s.\n", pid_path); old_pid_len = strlen(old_pid_buffer); if (strlen(old_pid_buffer)) { switch_file_write(fd, old_pid_buffer, &old_pid_len); } return 255; } switch_file_write(fd, pid_buffer, &pid_len); if (switch_core_init_and_modload(flags, nc ? SWITCH_FALSE : SWITCH_TRUE, &err) != SWITCH_STATUS_SUCCESS) { fprintf(stderr, "Cannot Initialize [%s]\n", err); return 255; } #ifndef WIN32 if (do_wait) { if (fds[1] > -1) { int i, v = 1; if ((i = write(fds[1], &v, sizeof(v))) < 0) { fprintf(stderr, "System Error [%s]\n", strerror(errno)); } else { (void)read(fds[1], &v, sizeof(v)); } shutdown(fds[1], 2); close(fds[1]); fds[1] = -1; } } #endif if (nc && nf) { signal(SIGINT, handle_SIGILL); } switch_core_runtime_loop(nc); destroy_status = switch_core_destroy(); switch_file_close(fd); apr_pool_destroy(pool); if (unlink(pid_path) != 0) { fprintf(stderr, "Failed to delete pid file [%s]\n", pid_path); } if (destroy_status == SWITCH_STATUS_RESTART) { char buf[1024] = { 0 }; int j = 0; switch_sleep(1000000); if (!argv || execv(argv[0], argv) == -1) { fprintf(stderr, "Restart Failed [%s] resorting to plan b\n", strerror(errno)); for (j = 0; j < argc; j++) { switch_snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "%s ", argv[j]); } ret = system(buf); } } return ret; } /* For Emacs: * Local Variables: * mode:c * indent-tabs-mode:t * tab-width:4 * c-basic-offset:4 * End: * For VIM: * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet: */help
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?86tvuor6bt.fsf>
