Skip site navigation (1)Skip section navigation (2)
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, &param);
		param.sched_priority = thread->priority;
		pthread_setschedparam(tt, policy, &param);
	}
#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>