forked from Mirrors/freeswitch
Merge branch 'master' of ssh://stash.freeswitch.org:7999/fs/freeswitch
This commit is contained in:
commit
0085bd68b7
|
@ -105,6 +105,7 @@ event_handlers/mod_event_socket
|
|||
#event_handlers/mod_format_cdr
|
||||
#event_handlers/mod_json_cdr
|
||||
#event_handlers/mod_radius_cdr
|
||||
#event_handlers/mod_odbc_cdr
|
||||
#event_handlers/mod_rayo
|
||||
#event_handlers/mod_snmp
|
||||
formats/mod_local_stream
|
||||
|
|
|
@ -1558,6 +1558,7 @@ AC_CONFIG_FILES([Makefile
|
|||
src/mod/event_handlers/mod_format_cdr/Makefile
|
||||
src/mod/event_handlers/mod_json_cdr/Makefile
|
||||
src/mod/event_handlers/mod_radius_cdr/Makefile
|
||||
src/mod/event_handlers/mod_odbc_cdr/Makefile
|
||||
src/mod/event_handlers/mod_rayo/Makefile
|
||||
src/mod/event_handlers/mod_snmp/Makefile
|
||||
src/mod/event_handlers/mod_event_zmq/Makefile
|
||||
|
@ -1586,7 +1587,7 @@ AC_CONFIG_FILES([Makefile
|
|||
src/mod/say/mod_say_de/Makefile
|
||||
src/mod/say/mod_say_en/Makefile
|
||||
src/mod/say/mod_say_es/Makefile
|
||||
src/mod/say/mod_say_es_ar/Makefile
|
||||
src/mod/say/mod_say_es_ar/Makefile
|
||||
src/mod/say/mod_say_fa/Makefile
|
||||
src/mod/say/mod_say_fr/Makefile
|
||||
src/mod/say/mod_say_he/Makefile
|
||||
|
|
|
@ -333,9 +333,9 @@ Description: mod_opus
|
|||
Adds mod_opus.
|
||||
|
||||
Module: codecs/mod_sangoma_codec
|
||||
Build-Depends: libsngtc-dev
|
||||
Description: mod_sangoma_codec
|
||||
Adds mod_sangoma_codec.
|
||||
Build-Depends: libsngtc-dev
|
||||
|
||||
Module: codecs/mod_silk
|
||||
Description: mod_silk
|
||||
|
@ -500,6 +500,10 @@ Module: event_handlers/mod_json_cdr
|
|||
Description: mod_json_cdr
|
||||
Adds mod_json_cdr.
|
||||
|
||||
Module: event_handlers/mod_odbc_cdr
|
||||
Description: mod_odbc_cdr
|
||||
Adds mod_odbc_cdr.
|
||||
|
||||
Module: event_handlers/mod_radius_cdr
|
||||
Description: mod_radius_cdr
|
||||
Adds mod_radius_cdr.
|
||||
|
@ -630,6 +634,10 @@ Module: say/mod_say_es
|
|||
Description: mod_say_es
|
||||
Adds mod_say_es.
|
||||
|
||||
Module: say/mod_say_es_ar
|
||||
Description: mod_say_es_ar
|
||||
Adds mod_say_es_ar.
|
||||
|
||||
Module: say/mod_say_fa
|
||||
Description: mod_say_fa
|
||||
Adds mod_say_fa.
|
||||
|
|
|
@ -19,8 +19,7 @@ DESC=freeswitch
|
|||
NAME=freeswitch
|
||||
DAEMON=/usr/bin/freeswitch
|
||||
USER=freeswitch
|
||||
GROUP=freeswitch
|
||||
DAEMON_ARGS="-u $USER -g $GROUP -ncwait"
|
||||
DAEMON_ARGS="-u $USER -ncwait"
|
||||
CONFDIR=/etc/$NAME
|
||||
RUNDIR=/var/run/$NAME
|
||||
PIDFILE=$RUNDIR/$NAME.pid
|
||||
|
@ -43,7 +42,7 @@ do_start() {
|
|||
|
||||
# Directory in /var/run may disappear on reboot (e.g. when tmpfs used for /var/run).
|
||||
mkdir -p $RUNDIR
|
||||
chown -R $USER:$GROUP $RUNDIR
|
||||
chown -R $USER: $RUNDIR
|
||||
chmod -R ug=rwX,o= $RUNDIR
|
||||
|
||||
start-stop-daemon --start --quiet \
|
||||
|
|
|
@ -55,7 +55,6 @@ clean:
|
|||
dh $@
|
||||
|
||||
override_dh_auto_clean:
|
||||
dh_clean
|
||||
|
||||
.stamp-bootstrap:
|
||||
@$(call show_vars)
|
||||
|
|
|
@ -129,6 +129,7 @@ Source10: http://files.freeswitch.org/downloads/libs/libmemcached-0.32.tar.gz
|
|||
Source11: http://files.freeswitch.org/downloads/libs/json-c-0.9.tar.gz
|
||||
Source12: http://files.freeswitch.org/downloads/libs/opus-1.1-p2.tar.gz
|
||||
Source13: http://files.freeswitch.org/downloads/libs/v8-3.24.14.tar.bz2
|
||||
Source14: http://files.freeswitch.org/downloads/libs/mongo-c-driver-0.92.2.tar.gz
|
||||
Prefix: %{prefix}
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
#!/usr/bin/perl
|
||||
#
|
||||
# Make bulk gateway xml from csv file.
|
||||
#
|
||||
|
||||
open(CSV, "gateways.csv");
|
||||
my @data = <CSV>;
|
||||
close(CSV);
|
||||
|
||||
foreach my $line (@data) {
|
||||
chomp($line);
|
||||
my ($gwname, $username, $password) = split(/,/, $line);
|
||||
print <<XML;
|
||||
<gateway name="$gwname">
|
||||
<param name="username" value="$username"/>
|
||||
<!--<param name="realm" value="asterlink.com"/>-->
|
||||
<!--<param name="from-user" value="cluecon"/>-->
|
||||
<!--<param name="from-domain" value="asterlink.com"/>-->
|
||||
<param name="password" value="$password"/>
|
||||
<!--<param name="extension" value="cluecon"/>-->
|
||||
<!--<param name="proxy" value="asterlink.com"/>-->
|
||||
<!--<param name="register-proxy" value="mysbc.com"/>-->
|
||||
<!--<param name="expire-seconds" value="60"/>-->
|
||||
<!--<param name="register" value="false"/>-->
|
||||
<!--<param name="register-transport" value="udp"/>-->
|
||||
<!--<param name="retry-seconds" value="30"/>-->
|
||||
<!--<param name="caller-id-in-from" value="false"/>-->
|
||||
<!--<param name="contact-params" value=""/>-->
|
||||
<!--<param name="extension-in-contact" value="true"/>-->
|
||||
<!--<param name="ping" value="25"/>-->
|
||||
<!--<param name="cid-type" value="rpid"/>-->
|
||||
<!--<param name="rfc-5626" value="true"/>-->
|
||||
<!--<param name="reg-id" value="1"/>-->
|
||||
</gateway>
|
||||
XML
|
||||
|
||||
}
|
|
@ -541,6 +541,28 @@ SWITCH_DECLARE(int) switch_build_uri(char *uri, switch_size_t size, const char *
|
|||
|
||||
#define SWITCH_STATUS_IS_BREAK(x) (x == SWITCH_STATUS_BREAK || x == 730035 || x == 35 || x == SWITCH_STATUS_INTR)
|
||||
|
||||
|
||||
#ifdef _MSC_VER
|
||||
|
||||
#define switch_errno() WSAGetLastError()
|
||||
|
||||
static inline int switch_errno_is_break(int errcode)
|
||||
{
|
||||
return errcode == WSAEWOULDBLOCK || errcode == WSAEINPROGRESS || errcode == WSAEINTR;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#define switch_errno() errno
|
||||
|
||||
static inline int switch_errno_is_break(int errcode)
|
||||
{
|
||||
return errcode == EAGAIN || errcode == EWOULDBLOCK || errcode == EINPROGRESS || errcode == EINTR || errcode == ETIMEDOUT;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/*!
|
||||
\brief Return a printable name of a switch_priority_t
|
||||
\param priority the priority to get the name of
|
||||
|
|
|
@ -2277,7 +2277,8 @@ static switch_status_t conference_add_member(conference_obj_t *conference, confe
|
|||
}
|
||||
|
||||
if (conference->count > 1) {
|
||||
if (conference->moh_sound && !switch_test_flag(conference, CFLAG_WAIT_MOD)) {
|
||||
if ((conference->moh_sound && !switch_test_flag(conference, CFLAG_WAIT_MOD)) ||
|
||||
(switch_test_flag(conference, CFLAG_WAIT_MOD) && !switch_true(switch_channel_get_variable(channel, "conference_permanent_wait_mod_moh")))) {
|
||||
/* stop MoH if any */
|
||||
conference_stop_file(conference, FILE_STOP_ASYNC);
|
||||
}
|
||||
|
@ -2287,10 +2288,9 @@ static switch_status_t conference_add_member(conference_obj_t *conference, confe
|
|||
if (switch_test_flag(conference, CFLAG_ENTER_SOUND)) {
|
||||
if (!zstr(enter_sound)) {
|
||||
conference_play_file(conference, (char *)enter_sound, CONF_DEFAULT_LEADIN,
|
||||
switch_core_session_get_channel(member->session), !switch_test_flag(conference, CFLAG_WAIT_MOD) ? 0 : 1);
|
||||
switch_core_session_get_channel(member->session), 0);
|
||||
} else {
|
||||
conference_play_file(conference, conference->enter_sound, CONF_DEFAULT_LEADIN, switch_core_session_get_channel(member->session),
|
||||
!switch_test_flag(conference, CFLAG_WAIT_MOD) ? 0 : 1);
|
||||
conference_play_file(conference, conference->enter_sound, CONF_DEFAULT_LEADIN, switch_core_session_get_channel(member->session), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2316,7 +2316,7 @@ static switch_status_t conference_add_member(conference_obj_t *conference, confe
|
|||
if (conference->alone_sound && !switch_test_flag(member, MFLAG_GHOST)) {
|
||||
conference_stop_file(conference, FILE_STOP_ASYNC);
|
||||
conference_play_file(conference, conference->alone_sound, CONF_DEFAULT_LEADIN,
|
||||
switch_core_session_get_channel(member->session), 1);
|
||||
switch_core_session_get_channel(member->session), 0);
|
||||
} else {
|
||||
switch_snprintf(msg, sizeof(msg), "You are currently the only person in this conference.");
|
||||
conference_member_say(member, msg, CONF_DEFAULT_LEADIN);
|
||||
|
@ -2683,7 +2683,7 @@ static switch_status_t conference_del_member(conference_obj_t *conference, confe
|
|||
|
||||
if (member->session && (exit_sound = switch_channel_get_variable(switch_core_session_get_channel(member->session), "conference_exit_sound"))) {
|
||||
conference_play_file(conference, (char *)exit_sound, CONF_DEFAULT_LEADIN,
|
||||
switch_core_session_get_channel(member->session), !switch_test_flag(conference, CFLAG_WAIT_MOD) ? 0 : 1);
|
||||
switch_core_session_get_channel(member->session), 0);
|
||||
}
|
||||
|
||||
|
||||
|
@ -2786,12 +2786,16 @@ static switch_status_t conference_del_member(conference_obj_t *conference, confe
|
|||
|| (switch_test_flag(conference, CFLAG_DYNAMIC) && (conference->count + conference->count_ghosts == 0))) {
|
||||
switch_set_flag(conference, CFLAG_DESTRUCT);
|
||||
} else {
|
||||
if (!switch_true(switch_channel_get_variable(channel, "conference_permanent_wait_mod_moh")) && switch_test_flag(conference, CFLAG_WAIT_MOD)) {
|
||||
/* Stop MOH if any */
|
||||
conference_stop_file(conference, FILE_STOP_ASYNC);
|
||||
}
|
||||
if (!exit_sound && conference->exit_sound && switch_test_flag(conference, CFLAG_EXIT_SOUND)) {
|
||||
conference_play_file(conference, conference->exit_sound, 0, channel, 0);
|
||||
}
|
||||
if (conference->count == 1 && conference->alone_sound && !switch_test_flag(conference, CFLAG_WAIT_MOD) && !switch_test_flag(member, MFLAG_GHOST)) {
|
||||
conference_stop_file(conference, FILE_STOP_ASYNC);
|
||||
conference_play_file(conference, conference->alone_sound, 0, channel, 1);
|
||||
conference_play_file(conference, conference->alone_sound, 0, channel, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3146,7 +3150,7 @@ static void *SWITCH_THREAD_FUNC conference_thread_run(switch_thread_t *thread, v
|
|||
if (conference->perpetual_sound && !conference->async_fnode) {
|
||||
conference_play_file(conference, conference->perpetual_sound, CONF_DEFAULT_LEADIN, NULL, 1);
|
||||
} else if (conference->moh_sound && ((nomoh == 0 && conference->count == 1)
|
||||
|| switch_test_flag(conference, CFLAG_WAIT_MOD)) && !conference->async_fnode) {
|
||||
|| switch_test_flag(conference, CFLAG_WAIT_MOD)) && !conference->async_fnode && !conference->fnode) {
|
||||
conference_play_file(conference, conference->moh_sound, CONF_DEFAULT_LEADIN, NULL, 1);
|
||||
}
|
||||
|
||||
|
|
|
@ -79,6 +79,15 @@ SWITCH_MODULE_DEFINITION(mod_fifo, mod_fifo_load, mod_fifo_shutdown, NULL);
|
|||
* The /enterprise/ outbound strategy does not preserve the caller ID
|
||||
* of the caller thereby allowing deliver of callers to agents at the
|
||||
* fastest possible rate.
|
||||
*
|
||||
* outbound_per_cycle is used to define the maximum number of agents
|
||||
* who will be available to answer a single caller. In ringall this
|
||||
* maximum is the number who will be called, in enterprise the need defines
|
||||
* how many agents will be called. outbound_per_cycle_min will define
|
||||
* the minimum agents who will be called to answer a caller regardless of
|
||||
* need, giving the enterprise strategy the ability to ring through more
|
||||
* than one agent for one caller.
|
||||
|
||||
*
|
||||
* ## Manual calls
|
||||
*
|
||||
|
@ -391,6 +400,7 @@ struct fifo_node {
|
|||
long busy;
|
||||
int is_static;
|
||||
int outbound_per_cycle;
|
||||
int outbound_per_cycle_min;
|
||||
char *outbound_name;
|
||||
outbound_strategy_t outbound_strategy;
|
||||
int ring_timeout;
|
||||
|
@ -1985,6 +1995,21 @@ static int place_call_enterprise_callback(void *pArg, int argc, char **argv, cha
|
|||
* the results. The enterprise strategy handler can simply take each
|
||||
* member one at a time, so the `place_call_enterprise_callback` takes
|
||||
* care of invoking the handler.
|
||||
*
|
||||
* Within the ringall call strategy outbound_per_cycle is used to define
|
||||
* how many agents exactly are assigned to the caller. With ringall if
|
||||
* multiple callers are calling in and one is answered, because the call
|
||||
* is assigned to all agents the call to the agents that is not answered
|
||||
* will be lose raced and the other agents will drop the call before the
|
||||
* next one will begin to ring. When oubound_per_cycle is used in the
|
||||
* enterprise strategy it acts as a maximum value for how many agents
|
||||
* are rung at once on any call, the caller is not assigned to any agent
|
||||
* until the call is answered. Enterprise only rings the number of phones
|
||||
* that are needed, so outbound_per_cycle as a max does not give you the
|
||||
* effect of ringall. outbound_per_cycle_min defines how many agents minimum
|
||||
* will be rung by an incoming caller through fifo, which can give a ringall
|
||||
* effect. outbound_per_cycle and outbound_per_cycle_min both default to 1.
|
||||
*
|
||||
*/
|
||||
static void find_consumers(fifo_node_t *node)
|
||||
{
|
||||
|
@ -2005,6 +2030,8 @@ static void find_consumers(fifo_node_t *node)
|
|||
|
||||
if (node->outbound_per_cycle && node->outbound_per_cycle < need) {
|
||||
need = node->outbound_per_cycle;
|
||||
} else if (node->outbound_per_cycle_min && node->outbound_per_cycle_min > need) {
|
||||
need = node->outbound_per_cycle_min;
|
||||
}
|
||||
|
||||
fifo_execute_sql_callback(globals.sql_mutex, sql, place_call_enterprise_callback, &need);
|
||||
|
@ -4045,6 +4072,9 @@ static void list_node(fifo_node_t *node, switch_xml_t x_report, int *off, int ve
|
|||
switch_snprintf(tmp, sizeof(buffer), "%u", node->outbound_per_cycle);
|
||||
switch_xml_set_attr_d(x_fifo, "outbound_per_cycle", tmp);
|
||||
|
||||
switch_snprintf(tmp, sizeof(buffer), "%u", node->outbound_per_cycle_min);
|
||||
switch_xml_set_attr_d(x_fifo, "outbound_per_cycle_min", tmp);
|
||||
|
||||
switch_snprintf(tmp, sizeof(buffer), "%u", node->ring_timeout);
|
||||
switch_xml_set_attr_d(x_fifo, "ring_timeout", tmp);
|
||||
|
||||
|
@ -4088,6 +4118,7 @@ void node_dump(switch_stream_handle_t *stream)
|
|||
stream->write_function(stream, "node: %s\n"
|
||||
" outbound_name: %s\n"
|
||||
" outbound_per_cycle: %d"
|
||||
" outbound_per_cycle_min: %d"
|
||||
" outbound_priority: %d"
|
||||
" outbound_strategy: %s\n"
|
||||
" has_outbound: %d\n"
|
||||
|
@ -4096,7 +4127,7 @@ void node_dump(switch_stream_handle_t *stream)
|
|||
" ready: %d\n"
|
||||
" waiting: %d\n"
|
||||
,
|
||||
node->name, node->outbound_name, node->outbound_per_cycle,
|
||||
node->name, node->outbound_name, node->outbound_per_cycle, node->outbound_per_cycle_min,
|
||||
node->outbound_priority, print_strategy(node->outbound_strategy),
|
||||
node->has_outbound,
|
||||
node->outbound_priority,
|
||||
|
@ -4508,6 +4539,13 @@ static switch_status_t load_config(int reload, int del_all)
|
|||
node->has_outbound = 1;
|
||||
}
|
||||
|
||||
node->outbound_per_cycle_min = 1;
|
||||
if ((val = switch_xml_attr(fifo, "outbound_per_cycle_min"))) {
|
||||
if (!((i = atoi(val)) < 0)) {
|
||||
node->outbound_per_cycle_min = i;
|
||||
}
|
||||
}
|
||||
|
||||
if ((val = switch_xml_attr(fifo, "retry_delay"))) {
|
||||
if ((i = atoi(val)) < 0) i = 0;
|
||||
node->retry_delay = i;
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
include $(top_srcdir)/build/modmake.rulesam
|
||||
MODNAME=mod_odbc_cdr
|
||||
|
||||
mod_LTLIBRARIES = mod_odbc_cdr.la
|
||||
mod_odbc_cdr_la_SOURCES = mod_odbc_cdr.c
|
||||
mod_odbc_cdr_la_CFLAGS = $(AM_CFLAGS)
|
||||
mod_odbc_cdr_la_LIBADD = $(switch_builddir)/libfreeswitch.la
|
||||
mod_odbc_cdr_la_LDFLAGS = -avoid-version -module -no-undefined -shared
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
<configuration name="odbc_cdr.conf" description="ODBC CDR Configuration">
|
||||
<settings>
|
||||
<!-- <param name="odbc-dsn" value="database:username:password"/> -->
|
||||
<param name="odbc-dsn" value="pgsql://hostaddr=192.168.0.100 dbname=freeswitch user=freeswitch password='freeswitch' options='-c client_min_messages=NOTICE'"/>
|
||||
<!-- global value can be "a-leg", "b-leg", "both" (default is "both") -->
|
||||
<param name="log-leg" value="both"/>
|
||||
<!-- value can be "always", "never", "on-db-fail" -->
|
||||
<param name="write-csv" value="on-db-fail"/>
|
||||
<!-- location to store csv copy of CDR -->
|
||||
<param name="csv-path" value="/usr/local/freeswitch/log/odbc_cdr"/>
|
||||
<!-- if "csv-path-on-fail" is set, failed INSERTs will be placed here as CSV files otherwise they will be placed in "csv-path" -->
|
||||
<param name="csv-path-on-fail" value="/usr/local/freeswitch/log/odbc_cdr/failed"/>
|
||||
<!-- dump SQL statement after leg ends -->
|
||||
<param name="debug-sql" value="true"/>
|
||||
</settings>
|
||||
<tables>
|
||||
<!-- only a-legs will be inserted into this table -->
|
||||
<table name="cdr_table_a_leg" log-leg="a-leg">
|
||||
<field name="CallId" chan-var-name="call_uuid"/>
|
||||
<field name="orig_id" chan-var-name="uuid"/>
|
||||
<field name="term_id" chan-var-name="sip_call_id"/>
|
||||
<field name="ClientId" chan-var-name="uuid"/>
|
||||
<field name="IP" chan-var-name="sip_network_ip"/>
|
||||
<field name="IPInternal" chan-var-name="sip_via_host"/>
|
||||
<field name="CODEC" chan-var-name="read_codec"/>
|
||||
<field name="directGateway" chan-var-name="sip_req_host"/>
|
||||
<field name="redirectGateway" chan-var-name="sip_redirect_contact_host_0"/>
|
||||
<field name="CallerID" chan-var-name="sip_from_user"/>
|
||||
<field name="TelNumber" chan-var-name="sip_req_user"/>
|
||||
<field name="TelNumberFull" chan-var-name="sip_to_user"/>
|
||||
<field name="sip_endpoint_disposition" chan-var-name="endpoint_disposition"/>
|
||||
<field name="sip_current_application" chan-var-name="current_application"/>
|
||||
</table>
|
||||
<!-- only b-legs will be inserted into this table -->
|
||||
<table name="cdr_table_b_leg" log-leg="b-leg">
|
||||
<field name="CallId" chan-var-name="call_uuid"/>
|
||||
<field name="orig_id" chan-var-name="uuid"/>
|
||||
<field name="term_id" chan-var-name="sip_call_id"/>
|
||||
<field name="ClientId" chan-var-name="uuid"/>
|
||||
<field name="IP" chan-var-name="sip_network_ip"/>
|
||||
<field name="IPInternal" chan-var-name="sip_via_host"/>
|
||||
<field name="CODEC" chan-var-name="read_codec"/>
|
||||
<field name="directGateway" chan-var-name="sip_req_host"/>
|
||||
<field name="redirectGateway" chan-var-name="sip_redirect_contact_host_0"/>
|
||||
<field name="CallerID" chan-var-name="sip_from_user"/>
|
||||
<field name="TelNumber" chan-var-name="sip_req_user"/>
|
||||
<field name="TelNumberFull" chan-var-name="sip_to_user"/>
|
||||
<field name="sip_endpoint_disposition" chan-var-name="endpoint_disposition"/>
|
||||
<field name="sip_current_application" chan-var-name="current_application"/>
|
||||
</table>
|
||||
<!-- both legs will be inserted into this table -->
|
||||
<table name="cdr_table_both">
|
||||
<field name="CallId" chan-var-name="uuid"/>
|
||||
<field name="orig_id" chan-var-name="Caller-Unique-ID"/>
|
||||
<field name="TEST_id" chan-var-name="sip_from_uri"/>
|
||||
</table>
|
||||
</tables>
|
||||
</configuration>
|
|
@ -0,0 +1,565 @@
|
|||
/*
|
||||
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
|
||||
* Copyright (C) 2005-2012, 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):
|
||||
*
|
||||
* Emmanuel Schmidbauer <e.schmidbauer@gmail.com>
|
||||
*
|
||||
* mod_odbc_cdr.c
|
||||
*
|
||||
*/
|
||||
|
||||
#include "switch.h"
|
||||
|
||||
#define ODBC_CDR_SQLITE_DB_NAME "odbc_cdr"
|
||||
|
||||
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_odbc_cdr_shutdown);
|
||||
SWITCH_MODULE_LOAD_FUNCTION(mod_odbc_cdr_load);
|
||||
SWITCH_MODULE_DEFINITION(mod_odbc_cdr, mod_odbc_cdr_load, mod_odbc_cdr_shutdown, NULL);
|
||||
|
||||
static const char *global_cf = "odbc_cdr.conf";
|
||||
|
||||
typedef enum {
|
||||
ODBC_CDR_LOG_A,
|
||||
ODBC_CDR_LOG_B,
|
||||
ODBC_CDR_LOG_BOTH
|
||||
} odbc_cdr_log_leg_t;
|
||||
|
||||
typedef enum {
|
||||
ODBC_CDR_CSV_ALWAYS,
|
||||
ODBC_CDR_CSV_NEVER,
|
||||
ODBC_CDR_CSV_ON_FAIL
|
||||
} odbc_cdr_write_csv_t;
|
||||
|
||||
static struct {
|
||||
char *odbc_dsn;
|
||||
char *dbname;
|
||||
char *csv_path;
|
||||
char *csv_fail_path;
|
||||
odbc_cdr_log_leg_t log_leg;
|
||||
odbc_cdr_write_csv_t write_csv;
|
||||
switch_bool_t debug_sql;
|
||||
switch_hash_t *table_hash;
|
||||
uint32_t running;
|
||||
switch_mutex_t *mutex;
|
||||
switch_memory_pool_t *pool;
|
||||
} globals;
|
||||
|
||||
struct table_profile {
|
||||
char *name;
|
||||
odbc_cdr_log_leg_t log_leg;
|
||||
switch_hash_t *field_hash;
|
||||
uint32_t flags;
|
||||
switch_mutex_t *mutex;
|
||||
switch_memory_pool_t *pool;
|
||||
};
|
||||
typedef struct table_profile table_profile_t;
|
||||
|
||||
static table_profile_t *load_table(const char *table_name)
|
||||
{
|
||||
table_profile_t *table = NULL;
|
||||
switch_xml_t x_tables, cfg, xml, x_table, x_field;
|
||||
|
||||
if (!(xml = switch_xml_open_cfg(global_cf, &cfg, NULL))) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Open of %s failed\n", global_cf);
|
||||
return table;
|
||||
}
|
||||
|
||||
if (!(x_tables = switch_xml_child(cfg, "tables"))) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
if ((x_table = switch_xml_find_child(x_tables, "table", "name", table_name))) {
|
||||
switch_memory_pool_t *pool;
|
||||
char *table_log_leg = (char *) switch_xml_attr_soft(x_table, "log-leg");
|
||||
|
||||
if (switch_core_new_memory_pool(&pool) != SWITCH_STATUS_SUCCESS) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Pool Failure\n");
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (!(table = switch_core_alloc(pool, sizeof(table_profile_t)))) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Alloc Failure\n");
|
||||
switch_core_destroy_memory_pool(&pool);
|
||||
goto end;
|
||||
}
|
||||
|
||||
table->pool = pool;
|
||||
|
||||
switch_mutex_init(&table->mutex, SWITCH_MUTEX_NESTED, table->pool);
|
||||
|
||||
table->name = switch_core_strdup(pool, table_name);
|
||||
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Found table [%s]\n", table->name);
|
||||
|
||||
if (!strcasecmp(table_log_leg, "a-leg")) {
|
||||
table->log_leg = ODBC_CDR_LOG_A;
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set table [%s] to log A-legs only\n", table->name);
|
||||
} else if (!strcasecmp(table_log_leg, "b-leg")) {
|
||||
table->log_leg = ODBC_CDR_LOG_B;
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set table [%s] to log B-legs only\n", table->name);
|
||||
} else {
|
||||
table->log_leg = ODBC_CDR_LOG_BOTH;
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set table [%s] to log both legs\n", table->name);
|
||||
}
|
||||
|
||||
switch_core_hash_init(&table->field_hash);
|
||||
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Adding fields to table [%s]\n", table->name);
|
||||
|
||||
for (x_field = switch_xml_child(x_table, "field"); x_field; x_field = x_field->next) {
|
||||
char *var = (char *) switch_xml_attr_soft(x_field, "name");
|
||||
char *val = (char *) switch_xml_attr_soft(x_field, "chan-var-name");
|
||||
char *value = NULL;
|
||||
if (zstr(var) || zstr(val)) {
|
||||
continue; // Ignore empty entries
|
||||
}
|
||||
value = switch_core_strdup(pool, val);
|
||||
switch_core_hash_insert_locked(table->field_hash, var, value, table->mutex);
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Field [%s] (%s) added to [%s]\n", var, val, table->name);
|
||||
}
|
||||
|
||||
switch_core_hash_insert(globals.table_hash, table->name, table);
|
||||
}
|
||||
|
||||
end:
|
||||
|
||||
if (xml) {
|
||||
switch_xml_free(xml);
|
||||
}
|
||||
|
||||
return table;
|
||||
}
|
||||
|
||||
switch_cache_db_handle_t *get_db_handle(void)
|
||||
{
|
||||
switch_cache_db_handle_t *dbh = NULL;
|
||||
char *dsn;
|
||||
if (!zstr(globals.odbc_dsn)) {
|
||||
dsn = globals.odbc_dsn;
|
||||
} else {
|
||||
dsn = globals.dbname;
|
||||
}
|
||||
if (switch_cache_db_get_db_handle_dsn(&dbh, dsn) != SWITCH_STATUS_SUCCESS) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error Opening DB\n");
|
||||
dbh = NULL;
|
||||
}
|
||||
return dbh;
|
||||
}
|
||||
|
||||
static switch_status_t odbc_cdr_execute_sql_no_callback(char *sql)
|
||||
{
|
||||
switch_cache_db_handle_t *dbh = NULL;
|
||||
switch_status_t status = SWITCH_STATUS_FALSE;
|
||||
|
||||
if (!(dbh = get_db_handle())) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error Opening DB\n");
|
||||
goto end;
|
||||
}
|
||||
|
||||
status = switch_cache_db_execute_sql(dbh, sql, NULL);
|
||||
|
||||
end:
|
||||
|
||||
switch_cache_db_release_db_handle(&dbh);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static void write_cdr(const char *path, const char *log_line)
|
||||
{
|
||||
int fd = -1;
|
||||
#ifdef _MSC_VER
|
||||
if ((fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR)) > -1) {
|
||||
#else
|
||||
if ((fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)) > -1) {
|
||||
#endif
|
||||
int wrote;
|
||||
wrote = write(fd, log_line, (unsigned) strlen(log_line));
|
||||
wrote += write(fd, "\n", 1);
|
||||
wrote++;
|
||||
close(fd);
|
||||
fd = -1;
|
||||
}
|
||||
}
|
||||
|
||||
static switch_status_t odbc_cdr_reporting(switch_core_session_t *session)
|
||||
{
|
||||
switch_channel_t *channel = switch_core_session_get_channel(session);
|
||||
switch_memory_pool_t *pool = switch_core_session_get_pool(session);
|
||||
switch_caller_profile_t *caller_profile = switch_channel_get_caller_profile(channel);
|
||||
switch_hash_index_t *hi;
|
||||
const void *var;
|
||||
void *val;
|
||||
switch_console_callback_match_t *matches = NULL;
|
||||
switch_console_callback_match_node_t *m;
|
||||
const char *uuid = NULL;
|
||||
|
||||
if (globals.log_leg == ODBC_CDR_LOG_A && caller_profile->direction == SWITCH_CALL_DIRECTION_OUTBOUND) {
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_NOTICE, "Only logging A-Leg, ignoring B-leg\n");
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
} else if (globals.log_leg == ODBC_CDR_LOG_B && caller_profile->direction == SWITCH_CALL_DIRECTION_INBOUND) {
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_NOTICE, "Only logging B-Leg, ignoring A-leg\n");
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
if (!(uuid = switch_channel_get_variable(channel, "uuid"))) {
|
||||
uuid = switch_core_strdup(pool, caller_profile->uuid);
|
||||
}
|
||||
|
||||
// copy all table names from global hash
|
||||
switch_mutex_lock(globals.mutex);
|
||||
for (hi = switch_core_hash_first(globals.table_hash); hi; hi = switch_core_hash_next(&hi)) {
|
||||
switch_core_hash_this(hi, &var, NULL, &val);
|
||||
switch_console_push_match(&matches, (const char *) var);
|
||||
}
|
||||
switch_mutex_unlock(globals.mutex);
|
||||
|
||||
if (matches) {
|
||||
table_profile_t *table = NULL;
|
||||
|
||||
// loop through table names
|
||||
for (m = matches->head; m; m = m->next) {
|
||||
char *table_name = m->val;
|
||||
switch_bool_t started = SWITCH_FALSE;
|
||||
switch_bool_t skip_leg = SWITCH_FALSE;
|
||||
|
||||
switch_mutex_lock(globals.mutex);
|
||||
table = switch_core_hash_find(globals.table_hash, table_name);
|
||||
switch_mutex_unlock(globals.mutex);
|
||||
|
||||
if (!table) {
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_NOTICE, "Table [%s] not found, ignoring leg\n", table_name);
|
||||
skip_leg = SWITCH_TRUE;
|
||||
}
|
||||
|
||||
if (table->log_leg == ODBC_CDR_LOG_A && caller_profile->direction == SWITCH_CALL_DIRECTION_OUTBOUND) {
|
||||
skip_leg = SWITCH_TRUE;
|
||||
}
|
||||
|
||||
if (table->log_leg == ODBC_CDR_LOG_B && caller_profile->direction == SWITCH_CALL_DIRECTION_INBOUND) {
|
||||
skip_leg = SWITCH_TRUE;
|
||||
}
|
||||
|
||||
if (skip_leg == SWITCH_FALSE) {
|
||||
switch_hash_index_t *i_hi = NULL;
|
||||
const void *i_var;
|
||||
void *i_val;
|
||||
char *field_hash_key;
|
||||
char *field_hash_val;
|
||||
char *sql = NULL;
|
||||
char *full_path = NULL;
|
||||
switch_stream_handle_t stream_field = { 0 };
|
||||
switch_stream_handle_t stream_value = { 0 };
|
||||
switch_bool_t insert_fail = SWITCH_FALSE;
|
||||
|
||||
SWITCH_STANDARD_STREAM(stream_field);
|
||||
SWITCH_STANDARD_STREAM(stream_value);
|
||||
|
||||
for (i_hi = switch_core_hash_first_iter( table->field_hash, i_hi); i_hi; i_hi = switch_core_hash_next(&i_hi)) {
|
||||
const char *tmp;
|
||||
switch_core_hash_this(i_hi, &i_var, NULL, &i_val);
|
||||
field_hash_key = (char *) i_var;
|
||||
field_hash_val = (char *) i_val;
|
||||
|
||||
if ((tmp = switch_channel_get_variable(channel, field_hash_val))) {
|
||||
if (started == SWITCH_FALSE) {
|
||||
stream_field.write_function(&stream_field, "%s", field_hash_key);
|
||||
stream_value.write_function(&stream_value, "'%s'", tmp);
|
||||
} else {
|
||||
stream_field.write_function(&stream_field, ", %s", field_hash_key);
|
||||
stream_value.write_function(&stream_value, ", '%s'", tmp);
|
||||
}
|
||||
started = SWITCH_TRUE;
|
||||
}
|
||||
|
||||
}
|
||||
switch_safe_free(i_hi);
|
||||
|
||||
sql = switch_mprintf("INSERT INTO %s (%s) VALUES (%s)", table_name, stream_field.data, stream_value.data);
|
||||
if (globals.debug_sql == SWITCH_TRUE) {
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "sql %s\n", sql);
|
||||
}
|
||||
if (odbc_cdr_execute_sql_no_callback(sql) == SWITCH_STATUS_FALSE) {
|
||||
insert_fail = SWITCH_TRUE;
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Error executing query %s\n", sql);
|
||||
}
|
||||
|
||||
if (globals.write_csv == ODBC_CDR_CSV_ALWAYS) {
|
||||
if (insert_fail == SWITCH_TRUE) {
|
||||
full_path = switch_mprintf("%s%s%s.csv", globals.csv_fail_path, SWITCH_PATH_SEPARATOR, uuid);
|
||||
} else {
|
||||
full_path = switch_mprintf("%s%s%s.csv", globals.csv_path, SWITCH_PATH_SEPARATOR, uuid);
|
||||
}
|
||||
assert(full_path);
|
||||
write_cdr(full_path, stream_value.data);
|
||||
switch_safe_free(full_path);
|
||||
} else if (globals.write_csv == ODBC_CDR_CSV_ON_FAIL && insert_fail == SWITCH_TRUE) {
|
||||
full_path = switch_mprintf("%s%s%s.csv", globals.csv_fail_path, SWITCH_PATH_SEPARATOR, uuid);
|
||||
assert(full_path);
|
||||
write_cdr(full_path, stream_value.data);
|
||||
switch_safe_free(full_path);
|
||||
}
|
||||
|
||||
switch_safe_free(sql);
|
||||
|
||||
switch_safe_free(stream_field.data);
|
||||
switch_safe_free(stream_value.data);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
switch_console_free_matches(&matches);
|
||||
}
|
||||
|
||||
switch_safe_free(hi);
|
||||
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
switch_state_handler_table_t odbc_cdr_state_handlers = {
|
||||
/*.on_init */ NULL,
|
||||
/*.on_routing */ NULL,
|
||||
/*.on_execute */ NULL,
|
||||
/*.on_hangup */ NULL,
|
||||
/*.on_exchange_media */ NULL,
|
||||
/*.on_soft_execute */ NULL,
|
||||
/*.on_consume_media */ NULL,
|
||||
/*.on_hibernate */ NULL,
|
||||
/*.on_reset */ NULL,
|
||||
/*.on_park */ NULL,
|
||||
/*.on_reporting */ odbc_cdr_reporting,
|
||||
/*.on_destroy */ NULL
|
||||
};
|
||||
|
||||
static switch_status_t odbc_cdr_load_config(void)
|
||||
{
|
||||
switch_status_t status = SWITCH_STATUS_SUCCESS;
|
||||
switch_xml_t cfg, xml, settings, param, tables, table;
|
||||
switch_cache_db_handle_t *dbh = NULL;
|
||||
|
||||
switch_mutex_lock(globals.mutex);
|
||||
|
||||
if (!(xml = switch_xml_open_cfg(global_cf, &cfg, NULL))) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Open of %s failed\n", global_cf);
|
||||
status = SWITCH_STATUS_TERM;
|
||||
goto end;
|
||||
}
|
||||
|
||||
globals.debug_sql = SWITCH_FALSE;
|
||||
globals.log_leg = ODBC_CDR_LOG_BOTH;
|
||||
globals.write_csv = ODBC_CDR_CSV_NEVER;
|
||||
|
||||
if ((settings = switch_xml_child(cfg, "settings")) != NULL) {
|
||||
for (param = switch_xml_child(settings, "param"); param; param = param->next) {
|
||||
char *var = (char *) switch_xml_attr_soft(param, "name");
|
||||
char *val = (char *) switch_xml_attr_soft(param, "value");
|
||||
|
||||
if (zstr(var) || zstr(val)) {
|
||||
continue; // Ignore empty entries
|
||||
}
|
||||
if (!strcasecmp(var, "dbname")) {
|
||||
globals.dbname = strdup(val);
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Set dbname [%s]\n", globals.dbname);
|
||||
} else if (!strcasecmp(var, "odbc-dsn")) {
|
||||
globals.odbc_dsn = strdup(val);
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Set odbc-dsn [%s]\n", globals.odbc_dsn);
|
||||
} else if (!strcasecmp(var, "log-leg")) {
|
||||
if (!strcasecmp(val, "a-leg")) {
|
||||
globals.log_leg = ODBC_CDR_LOG_A;
|
||||
} else if (!strcasecmp(val, "b-leg")) {
|
||||
globals.log_leg = ODBC_CDR_LOG_B;
|
||||
}
|
||||
} else if (!strcasecmp(var, "debug-sql") && switch_true(val)) {
|
||||
globals.debug_sql = SWITCH_TRUE;
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Set debug-sql [true]\n");
|
||||
} else if (!strcasecmp(var, "write-csv") && !zstr(val)) {
|
||||
if (!strcasecmp(val, "always")) {
|
||||
globals.write_csv = ODBC_CDR_CSV_ALWAYS;
|
||||
} else if (!strcasecmp(val, "on-db-fail")) {
|
||||
globals.write_csv = ODBC_CDR_CSV_ON_FAIL;
|
||||
}
|
||||
} else if (!strcasecmp(var, "csv-path") && !zstr(val)) {
|
||||
globals.csv_path = switch_mprintf("%s%s", val, SWITCH_PATH_SEPARATOR);
|
||||
} else if (!strcasecmp(var, "csv-path-on-fail") && !zstr(val)) {
|
||||
globals.csv_fail_path = switch_mprintf("%s%s", val, SWITCH_PATH_SEPARATOR);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (globals.log_leg == ODBC_CDR_LOG_A) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Set log-leg [a-leg]\n");
|
||||
} else if (globals.log_leg == ODBC_CDR_LOG_B) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Set log-leg [b-leg]\n");
|
||||
} else {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Set log-leg [both]\n");
|
||||
}
|
||||
|
||||
if (!globals.csv_path) {
|
||||
globals.csv_path = switch_mprintf("%s%sodbc-cdr", SWITCH_GLOBAL_dirs.log_dir, SWITCH_PATH_SEPARATOR);
|
||||
}
|
||||
|
||||
if (!globals.csv_fail_path) {
|
||||
globals.csv_fail_path = switch_mprintf("%s%sodbc-cdr-failed", SWITCH_GLOBAL_dirs.log_dir, SWITCH_PATH_SEPARATOR);
|
||||
}
|
||||
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Set csv-path [%s]\n", globals.csv_path);
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Set csv-path-on-fail [%s]\n", globals.csv_fail_path);
|
||||
|
||||
if ((tables = switch_xml_child(cfg, "tables"))) {
|
||||
for (table = switch_xml_child(tables, "table"); table; table = table->next) {
|
||||
load_table(switch_xml_attr_soft(table, "name"));
|
||||
}
|
||||
}
|
||||
|
||||
if (!globals.dbname) {
|
||||
globals.dbname = strdup(ODBC_CDR_SQLITE_DB_NAME);
|
||||
}
|
||||
|
||||
// Initialize database
|
||||
if (!(dbh = get_db_handle())) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Cannot open DB!\n");
|
||||
status = SWITCH_STATUS_TERM;
|
||||
goto end;
|
||||
}
|
||||
|
||||
switch_cache_db_release_db_handle(&dbh);
|
||||
|
||||
end:
|
||||
switch_mutex_unlock(globals.mutex);
|
||||
|
||||
if (xml) {
|
||||
switch_xml_free(xml);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
SWITCH_MODULE_LOAD_FUNCTION(mod_odbc_cdr_load)
|
||||
{
|
||||
switch_status_t status;
|
||||
|
||||
memset(&globals, 0, sizeof(globals));
|
||||
switch_core_hash_init(&globals.table_hash);
|
||||
if (switch_mutex_init(&globals.mutex, SWITCH_MUTEX_NESTED, pool) != SWITCH_STATUS_SUCCESS) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "failed to initialize mutex\n");
|
||||
}
|
||||
globals.pool = pool;
|
||||
|
||||
if ((status = odbc_cdr_load_config()) != SWITCH_STATUS_SUCCESS) {
|
||||
return status;
|
||||
}
|
||||
|
||||
if (globals.write_csv != ODBC_CDR_CSV_NEVER) {
|
||||
if ((status = switch_dir_make_recursive(globals.csv_path, SWITCH_DEFAULT_DIR_PERMS, pool)) != SWITCH_STATUS_SUCCESS) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error creating %s\n", globals.csv_path);
|
||||
return status;
|
||||
}
|
||||
if (strcasecmp(globals.csv_path, globals.csv_fail_path)) {
|
||||
if ((status = switch_dir_make_recursive(globals.csv_fail_path, SWITCH_DEFAULT_DIR_PERMS, pool)) != SWITCH_STATUS_SUCCESS) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error creating %s\n", globals.csv_path);
|
||||
return status;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch_mutex_lock(globals.mutex);
|
||||
globals.running = 1;
|
||||
switch_mutex_unlock(globals.mutex);
|
||||
|
||||
switch_core_add_state_handler(&odbc_cdr_state_handlers);
|
||||
*module_interface = switch_loadable_module_create_module_interface(pool, modname);
|
||||
|
||||
/* indicate that the module should continue to be loaded */
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
Called when the system shuts down
|
||||
Macro expands to: switch_status_t mod_odbc_cdr_shutdown() */
|
||||
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_odbc_cdr_shutdown)
|
||||
{
|
||||
switch_hash_index_t *hi = NULL;
|
||||
table_profile_t *table;
|
||||
void *val = NULL;
|
||||
const void *key;
|
||||
switch_ssize_t keylen;
|
||||
|
||||
switch_mutex_lock(globals.mutex);
|
||||
if (globals.running == 1) {
|
||||
globals.running = 0;
|
||||
}
|
||||
|
||||
while ((hi = switch_core_hash_first_iter(globals.table_hash, hi))) {
|
||||
switch_hash_index_t *field_hi = NULL;
|
||||
void *field_val = NULL;
|
||||
const void *field_key;
|
||||
switch_ssize_t field_keylen;
|
||||
|
||||
switch_core_hash_this(hi, &key, &keylen, &val);
|
||||
table = (table_profile_t *) val;
|
||||
|
||||
while ((field_hi = switch_core_hash_first_iter(table->field_hash, field_hi))) {
|
||||
switch_core_hash_this(field_hi, &field_key, &field_keylen, &field_val);
|
||||
switch_core_hash_delete_locked(table->field_hash, field_key, table->mutex);
|
||||
}
|
||||
switch_core_hash_destroy(&table->field_hash);
|
||||
switch_safe_free(field_hi);
|
||||
|
||||
switch_core_hash_delete(globals.table_hash, table->name);
|
||||
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Destroying table %s\n", table->name);
|
||||
|
||||
switch_core_destroy_memory_pool(&table->pool);
|
||||
table = NULL;
|
||||
}
|
||||
switch_core_hash_destroy(&globals.table_hash);
|
||||
switch_safe_free(hi);
|
||||
|
||||
switch_safe_free(globals.csv_path)
|
||||
switch_safe_free(globals.csv_fail_path)
|
||||
switch_safe_free(globals.odbc_dsn);
|
||||
switch_safe_free(globals.dbname);
|
||||
|
||||
switch_mutex_unlock(globals.mutex);
|
||||
switch_mutex_destroy(globals.mutex);
|
||||
|
||||
switch_core_remove_state_handler(&odbc_cdr_state_handlers);
|
||||
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
/* 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
|
||||
*/
|
|
@ -2567,6 +2567,12 @@ SWITCH_DECLARE(int) switch_wait_sock(switch_os_socket_t sock, uint32_t ms, switc
|
|||
|
||||
s = poll(pfds, 1, ms);
|
||||
|
||||
if (s < 0) {
|
||||
if (switch_errno_is_break(switch_errno())) {
|
||||
s = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (s < 0) {
|
||||
r = s;
|
||||
} else if (s > 0) {
|
||||
|
@ -2645,6 +2651,12 @@ SWITCH_DECLARE(int) switch_wait_socklist(switch_waitlist_t *waitlist, uint32_t l
|
|||
|
||||
s = poll(pfds, len, ms);
|
||||
|
||||
if (s < 0) {
|
||||
if (switch_errno_is_break(switch_errno())) {
|
||||
s = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (s < 0) {
|
||||
r = s;
|
||||
} else if (s > 0) {
|
||||
|
@ -2758,6 +2770,12 @@ SWITCH_DECLARE(int) switch_wait_sock(switch_os_socket_t sock, uint32_t ms, switc
|
|||
|
||||
s = select(sock + 1, (flags & SWITCH_POLL_READ) ? rfds : NULL, (flags & SWITCH_POLL_WRITE) ? wfds : NULL, (flags & SWITCH_POLL_ERROR) ? efds : NULL, &tv);
|
||||
|
||||
if (s < 0) {
|
||||
if (switch_errno_is_break(switch_errno())) {
|
||||
s = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (s < 0) {
|
||||
r = s;
|
||||
} else if (s > 0) {
|
||||
|
@ -2858,6 +2876,12 @@ SWITCH_DECLARE(int) switch_wait_socklist(switch_waitlist_t *waitlist, uint32_t l
|
|||
|
||||
s = select(max_fd + 1, (flags & SWITCH_POLL_READ) ? rfds : NULL, (flags & SWITCH_POLL_WRITE) ? wfds : NULL, (flags & SWITCH_POLL_ERROR) ? efds : NULL, &tv);
|
||||
|
||||
if (s < 0) {
|
||||
if (switch_errno_is_break(switch_errno())) {
|
||||
s = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (s < 0) {
|
||||
r = s;
|
||||
} else if (s > 0) {
|
||||
|
|
Loading…
Reference in New Issue