[Freeswitch-svn] [commit] r11222 - freeswitch/trunk/scripts/contrib/dschreiber/mod_nibblebill
FreeSWITCH SVN
dschreiber at freeswitch.org
Thu Jan 15 03:31:57 PST 2009
Author: dschreiber
Date: Thu Jan 15 05:31:57 2009
New Revision: 11222
Log:
Fixing a nasty bug that was resulting in channels not being released on hangup if no billing cycles had taken place.
Modified:
freeswitch/trunk/scripts/contrib/dschreiber/mod_nibblebill/mod_nibblebill.c
Modified: freeswitch/trunk/scripts/contrib/dschreiber/mod_nibblebill/mod_nibblebill.c
==============================================================================
--- freeswitch/trunk/scripts/contrib/dschreiber/mod_nibblebill/mod_nibblebill.c (original)
+++ freeswitch/trunk/scripts/contrib/dschreiber/mod_nibblebill/mod_nibblebill.c Thu Jan 15 05:31:57 2009
@@ -71,7 +71,6 @@
switch_time_t pausets; /* Timestamp of when a pause action started. 0 if not paused */
float bill_adjustments; /* Adjustments to make to the next billing, based on pause/resume events */
- float manual_adjustments; /* Manually added/deducted amounts from this account, to be committed at next database query */
} nibble_data_t;
@@ -87,6 +86,9 @@
/* Keep track of our config, event hooks and database connection variables, for this module only */
static struct
{
+ /* Memory */
+ switch_memory_pool_t *pool;
+
/* Event hooks */
switch_event_node_t *node;
@@ -285,6 +287,32 @@
}
+/* At this time, billing never succeeds if you don't have a database. */
+static switch_status_t bill_event(float billamount, const char *billaccount)
+{
+#ifdef SWITCH_HAVE_ODBC
+ char sql[1024] = "";
+ nibblebill_results_t pdata;
+
+ memset(&pdata, 0, sizeof(pdata));
+ snprintf(sql, 1024, SQL_SAVE, globals.db_table, globals.db_column_cash, globals.db_column_cash, billamount, globals.db_column_account, billaccount);
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Doing update query\n[%s]\n", sql);
+
+ if (!(switch_odbc_handle_callback_exec(globals.master_odbc, sql, nibblebill_callback, &pdata) == SWITCH_ODBC_SUCCESS)){
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "DB Error while updating cash!\n");
+
+ } else {
+#endif
+ /* TODO: Failover to a flat/text file if DB is unavailable */
+
+ return SWITCH_STATUS_SUCCESS;
+#ifdef SWITCH_HAVE_ODBC
+ }
+#endif
+ return SWITCH_STATUS_SUCCESS;
+}
+
+
/* This is where we actually charge the guy
This can be called anytime a call is in progress or at the end of a call before the session is destroyed */
static switch_status_t do_billing(switch_core_session_t *session)
@@ -297,16 +325,16 @@
nibble_data_t *nibble_data;
switch_time_t ts = switch_timestamp_now();
float billamount;
- float adjustments = 0;
char date[80] = "";
char *tmp;
char *uuid;
switch_size_t retsize;
switch_time_exp_t tm;
+
if (!session) {
/* wtf? Why are we here? */
- return SWITCH_STATUS_GENERR;
+ return SWITCH_STATUS_SUCCESS;
}
uuid = switch_core_session_get_uuid(session);
@@ -314,16 +342,17 @@
/* Get channel var */
channel = switch_core_session_get_channel(session);
if (!channel) {
- return SWITCH_STATUS_GENERR;
+ return SWITCH_STATUS_SUCCESS;
}
/* Variables kept in FS but relevant only to this module */
const char *billrate = switch_channel_get_variable(channel, "nibble_rate");
const char *billaccount = switch_channel_get_variable(channel, "nibble_account");
+
/* Return if there's no billing information on this session */
if (!billrate || !billaccount) {
- return SWITCH_STATUS_GENERR;
+ return SWITCH_STATUS_SUCCESS;
}
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Attempting to bill at $%s per minute to account %s\n", billrate, billaccount);
@@ -333,7 +362,7 @@
if (!profile) {
/* No caller profile (why would this happen?) */
- return SWITCH_STATUS_GENERR;
+ return SWITCH_STATUS_SUCCESS;
}
if (profile->times->answered < 1) {
@@ -350,7 +379,7 @@
nibble_data = (nibble_data_t *) switch_channel_get_private(channel, "_nibble_data_");
/* Are we in paused mode? If so, we don't do anything here - go back! */
- if (nibble_data->pausets > 0) {
+ if (nibble_data && (nibble_data->pausets > 0)) {
if (globals.mutex) {
switch_mutex_unlock(globals.mutex);
}
@@ -368,6 +397,7 @@
/* Setup new billing data (based on call answer time, in case this module started late with active calls) */
nibble_data->lastts = profile->times->answered; /* Set the initial answer time to match when the call was really answered */
+ nibble_data->pausets = 0;
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Beginning new billing on %s\n", uuid);
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Last successful billing time was %s\n", date);
@@ -380,46 +410,23 @@
if ((ts - nibble_data->lastts) > 0) {
- /* Convert billrate into microseconds and multiply by # of microseconds that have passed */
+ /* Convert billrate into microseconds and multiply by # of microseconds that have passed since last *successful* bill */
billamount = (atof(billrate) / 1000000 / 60) * ((ts - nibble_data->lastts));
- adjustments = nibble_data->manual_adjustments + nibble_data->bill_adjustments;
- /* if ODBC call fails, we should return BEFORE updating the timestamp of last success! */
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Billing $%f to %s (Call: %s / %f so far)\n", billamount, billaccount, uuid, nibble_data->total);
- if (adjustments > 0) {
- switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "(Also adjusting the account by $%f)\n", adjustments);
- }
-
- /* DO ODBC BILLING HERE! */
-#ifdef SWITCH_HAVE_ODBC
- char sql[1024] = "";
- nibblebill_results_t pdata;
-
- memset(&pdata, 0, sizeof(pdata));
- snprintf(sql, 1024, SQL_SAVE, globals.db_table, globals.db_column_cash, globals.db_column_cash, (billamount - adjustments), globals.db_column_account, billaccount);
- switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Doing update query\n[%s]\n", sql);
-
- if (!(switch_odbc_handle_callback_exec(globals.master_odbc, sql, nibblebill_callback, &pdata) == SWITCH_ODBC_SUCCESS)){
- switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "DB Error while updating cash!\n");
-
- /* TODO: If this is a hangup event, we should store this billing in a text file while the DB is unavailable */
- } else {
-#endif
+ /* DO ODBC BILLING HERE and reset counters if it's successful! */
+ if (bill_event(billamount, billaccount) == SWITCH_STATUS_SUCCESS) {
/* Increment total cost */
nibble_data->total += billamount;
- /* Get rid of any manual adjustments that were just committed */
- nibble_data->manual_adjustments = 0;
- nibble_data->bill_adjustments = 0;
-
/* Update channel variable with current billing */
tmp = switch_mprintf("%f", nibble_data->total);
switch_channel_set_variable(channel, "nibble_total_billed", tmp);
switch_safe_free(tmp);
-#ifdef SWITCH_HAVE_ODBC
+ } else {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to log to database!\n");
}
-#endif
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Just tried to bill %s negative minutes! wtf? That should be impossible.\n", uuid);
}
@@ -427,8 +434,10 @@
/* Update the last time we billed */
nibble_data->lastts = ts;
- /* Save everything back */
- switch_channel_set_private(channel, "_nibble_data_", nibble_data);
+ /* Save this location, but only if the channel/session are not hungup (otherwise, we're done) */
+ if (channel && switch_channel_get_state(channel) != CS_HANGUP) {
+ switch_channel_set_private(channel, "_nibble_data_", nibble_data);
+ }
/* Done changing - release lock */
if (globals.mutex) {
@@ -437,9 +446,6 @@
/* Go check if this call is allowed to continue */
- /* Release the session lock */
- switch_core_session_rwunlock(session);
-
return SWITCH_STATUS_SUCCESS;
}
@@ -477,6 +483,7 @@
/* Go bill */
do_billing(session);
+
switch_core_session_rwunlock(session);
}
@@ -512,7 +519,6 @@
static void nibblebill_adjust(switch_core_session_t *session, float amount)
{
switch_channel_t *channel = switch_core_session_get_channel(session);
- nibble_data_t *nibble_data;
char *uuid;
if (!channel) {
@@ -521,53 +527,54 @@
uuid = switch_core_session_get_uuid(session);
- /* Lock this session's data for this module while we tinker with it */
- if (globals.mutex) {
- switch_mutex_lock(globals.mutex);
+ /* Get channel var */
+ if (!uuid || !channel) {
+ switch_core_session_rwunlock(session);
+ return;
}
- /* Get our nibble data var. This will be NULL if it's our first call here for this session */
- nibble_data = (nibble_data_t *) switch_channel_get_private(channel, "_nibble_data_");
-
- /*Add or remove amount from adjusted billing here */
- nibble_data->manual_adjustments += amount;
- switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Current adjustment for %s is $%f\n", uuid, amount);
+ /* Variables kept in FS but relevant only to this module */
+ const char *billaccount = switch_channel_get_variable(channel, "nibble_account");
- /* Done changing - release lock */
- if (globals.mutex) {
- switch_mutex_unlock(globals.mutex);
+ /* Return if there's no billing information on this session */
+ if (!billaccount) {
+ switch_core_session_rwunlock(session);
+ return;
}
- /* Release the session lock */
- switch_core_session_rwunlock(session);
-}
-
-#define CHECK_APP_SYNTAX ""
-SWITCH_STANDARD_APP(nibblebill_check_app_function)
-{
- float amount;
-
- amount = nibblebill_check(session);
+ /* Add or remove amount from adjusted billing here. Note, we bill the OPPOSITE */
+ if (bill_event(-amount, billaccount)) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Recorded adjustment to %s for $%f\n", billaccount, amount);
+ } else {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to record adjustment to %s for $%f\n", billaccount, amount);
+ }
- switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Current billing is at $%f\n", amount);
}
-#define ADJUST_APP_SYNTAX "<amount>"
-SWITCH_STANDARD_APP(nibblebill_adjust_app_function)
+#define APP_SYNTAX "pause | resume | reset | adjust <amount> | heartbeat <seconds> | check"
+SWITCH_STANDARD_APP(nibblebill_app_function)
{
- char *mydata = NULL;
int argc = 0;
- char *argv[4] = { 0 };
- float amount;
-
- if (!(mydata = switch_core_session_strdup(session, data))) {
- return;
- }
-
- if ((argc = switch_separate_string(mydata, ' ', argv, (sizeof(argv) / sizeof(argv[0]))))) {
- amount = atof(argv[0]);
- nibblebill_adjust(session, amount);
+ char *lbuf = NULL;
+ char *argv[3] = { 0 };
+
+ if (!switch_strlen_zero(data) && (lbuf = switch_core_session_strdup(session, data))
+ && (argc = switch_separate_string(lbuf, ' ', argv, (sizeof(argv) / sizeof(argv[0]))))) {
+ if (!strcasecmp(argv[0], "adjust") && argc == 2) {
+ nibblebill_adjust(session, atof(argv[1]));
+/* } else if (!strcasecmp(argv[0], "pause")) {
+ nibblebill_pause(session);
+ } else if (!strcasecmp(argv[0], "resume")) {
+ nibblebill_resume(session);*/
+ } else if (!strcasecmp(argv[0], "check")) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Current billing is at $%f\n", nibblebill_check(session));
+/* } else if (!strcasecmp(argv[0], "reset")) {
+ nibblebill_reset(session);*/
+ } else if (!strcasecmp(argv[0], "heartbeat") && argc == 2) {
+ switch_core_session_enable_heartbeat(session, atoi(argv[1]));
+ }
}
+
}
/* We get here from the API only (theoretically) */
@@ -605,6 +612,9 @@
/* Set every byte in this structure to 0 */
memset(&globals, 0, sizeof(globals));
+ globals.pool = pool;
+ switch_mutex_init(&globals.mutex, SWITCH_MUTEX_NESTED, globals.pool);
+
load_config();
/* connect my internal structure to the blank pointer passed to me */
@@ -618,11 +628,12 @@
SWITCH_ADD_API(api_interface, "check", "Check the balance of a session", nibblebill_check_api_function, CHECK_API_SYNTAX);
/* Add dialplan applications */
-/* SWITCH_ADD_APP(app_interface, "pause", "Pause billing on a session", "Pause billing on a session", nibblebill_pause_app_function, PAUSE_APP_SYNTAX, SAF_NONE);
- SWITCH_ADD_APP(app_interface, "resume", "Resume billing on a session", "Resume billing on a session", nibblebill_resume_app_function, RESUME_APP_SYNTAX, SAF_NONE);
- SWITCH_ADD_APP(app_interface, "reset", "Check the balance of a session", "Check the balance of a session", nibblebill_reset_app_function, RESET_APP_SYNTAX, SAF_NONE);*/
- SWITCH_ADD_APP(app_interface, "adjust", "Add or deduct a dollar amount from a caller's account", "Add or deduct a dollar amount from a callers' account (use negative numbers to deduct)", nibblebill_adjust_app_function, ADJUST_APP_SYNTAX, SAF_NONE);
- SWITCH_ADD_APP(app_interface, "check", "Check the balance on an account", "Perform an account check for fund balance on a specific caller's account", nibblebill_check_app_function, CHECK_APP_SYNTAX, SAF_NONE);
+/* SWITCH_ADD_APP(app_interface, "nibble_pause", "Pause billing on a session", "Pause billing on a session", nibblebill_pause_app_function, PAUSE_APP_SYNTAX, SAF_NONE);
+ SWITCH_ADD_APP(app_interface, "nibble_resume", "Resume billing on a session", "Resume billing on a session", nibblebill_resume_app_function, RESUME_APP_SYNTAX, SAF_NONE);
+ SWITCH_ADD_APP(app_interface, "nibble_reset", "Check the balance of a session", "Check the balance of a session", nibblebill_reset_app_function, RESET_APP_SYNTAX, SAF_NONE);
+ SWITCH_ADD_APP(app_interface, "nibble_adjust", "Add or deduct a dollar amount from a caller's account", "Add or deduct a dollar amount from a callers' account (use negative numbers to deduct)", nibblebill_adjust_app_function, ADJUST_APP_SYNTAX, SAF_NONE);
+ SWITCH_ADD_APP(app_interface, "nibble_check", "Check the balance on an account", "Perform an account check for fund balance on a specific caller's account", nibblebill_check_app_function, CHECK_APP_SYNTAX, SAF_NONE);*/
+ SWITCH_ADD_APP(app_interface, "nibblebill", "Handle billing for the current channel/call", "Pause, resume, reset, adjust, flush, heartbeat commands to handle billing.", nibblebill_app_function, APP_SYNTAX, SAF_NONE);
/* register state handlers for billing */
switch_core_add_state_handler(&nibble_state_handler);
More information about the Freeswitch-svn
mailing list