[Freeswitch-svn] [commit] r11223 - freeswitch/trunk/scripts/contrib/dschreiber/mod_nibblebill
FreeSWITCH SVN
dschreiber at freeswitch.org
Thu Jan 15 04:54:15 PST 2009
Author: dschreiber
Date: Thu Jan 15 06:54:15 2009
New Revision: 11223
Log:
Fixed a bunch of seg-faults, finished implementing all commands from CLI, API and dialplan.
Still needs some cleanup and consolidation, but functions and seems stable. Tested everything I could :-)
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 06:54:15 2009
@@ -40,14 +40,11 @@
* Thanks go to bandwidth.com for funding this work.
*
*
- * TODO: Create an override flag so you can force a start-time for when billing should count *from*
* TODO: Fix what happens when the DB is not available
* TODO: Fix what happens when the DB queries fail (right now, all are acting like success)
* TODO: Make the actions function for when funds are depleted
- * TODO: Add app and CLI commands (pause, resume, reset, flush, deduct_funds, add_funds)
* TODO: Add buffering abilities
* TODO: Make error handling for database, such that when the database is down (or not installed) we just log to a text file
- * TODO: Make a sample .XML config file!
* FUTURE: Possibly make the hooks not tied per-channel, and instead just do this as a supervision style application with one thread that watches all calls
*/
@@ -274,19 +271,6 @@
}
-static switch_status_t sched_billing(switch_core_session_t *session)
-{
-
- if (globals.global_heartbeat > 0) {
- switch_core_session_enable_heartbeat(session, globals.global_heartbeat);
- }
-
- // Check account balance here
-
- return SWITCH_STATUS_SUCCESS;
-
-}
-
/* At this time, billing never succeeds if you don't have a database. */
static switch_status_t bill_event(float billamount, const char *billaccount)
{
@@ -397,7 +381,6 @@
/* 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);
@@ -411,7 +394,7 @@
if ((ts - nibble_data->lastts) > 0) {
/* 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));
+ billamount = (atof(billrate) / 1000000 / 60) * ((ts - nibble_data->lastts)) - nibble_data->bill_adjustments;
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);
@@ -420,6 +403,9 @@
/* Increment total cost */
nibble_data->total += billamount;
+ /* Reset manual billing adjustments from pausing */
+ 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);
@@ -487,15 +473,14 @@
switch_core_session_rwunlock(session);
}
-
-static float nibblebill_check(switch_core_session_t *session)
+static void nibblebill_pause(switch_core_session_t *session)
{
switch_channel_t *channel = switch_core_session_get_channel(session);
+ switch_time_t ts = switch_timestamp_now();
nibble_data_t *nibble_data;
- float amount = 0;
if (!channel) {
- return -99999;
+ return;
}
/* Lock this session's data for this module while we tinker with it */
@@ -506,30 +491,140 @@
/* 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_");
- amount = nibble_data->total;
+ if (!nibble_data) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Can't pause - channel is not initialized for billing!\n");
+ return;
+ }
+
+ /* Set pause counter if not already set */
+ if (nibble_data->pausets == 0)
+ nibble_data->pausets = ts;
+
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Paused billing timestamp!\n");
/* Done checking - release lock */
if (globals.mutex) {
switch_mutex_unlock(globals.mutex);
}
- return amount;
}
-static void nibblebill_adjust(switch_core_session_t *session, float amount)
+static void nibblebill_resume(switch_core_session_t *session)
{
switch_channel_t *channel = switch_core_session_get_channel(session);
- char *uuid;
+ switch_time_t ts = switch_timestamp_now();
+ nibble_data_t *nibble_data;
if (!channel) {
return;
}
- uuid = switch_core_session_get_uuid(session);
+ /* 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_");
- /* Get channel var */
- if (!uuid || !channel) {
- switch_core_session_rwunlock(session);
+ if (!nibble_data) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Can't resume - channel is not initialized for billing (This is expected at hangup time)!\n");
+ return;
+ }
+
+ if (nibble_data->pausets == 0) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Can't resume - channel is not paused! (This is expected at hangup time)\n");
+ return;
+ }
+
+ /* Lock this session's data for this module while we tinker with it */
+ if (globals.mutex) {
+ switch_mutex_lock(globals.mutex);
+ }
+
+ const char *billrate = switch_channel_get_variable(channel, "nibble_rate");
+
+ /* Calculate how much was "lost" to billings during pause - we do this here because you never know when the billrate may change during a call */
+ nibble_data->bill_adjustments += (atof(billrate) / 1000000 / 60) * ((ts - nibble_data->pausets));
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Resumed billing! Subtracted %f from this billing cycle.\n", (atof(billrate) / 1000000 / 60) * ((ts - nibble_data->pausets)));
+
+ nibble_data->pausets = 0;
+
+ /* Done checking - release lock */
+ if (globals.mutex) {
+ switch_mutex_unlock(globals.mutex);
+ }
+
+}
+
+static void nibblebill_reset(switch_core_session_t *session)
+{
+ switch_channel_t *channel = switch_core_session_get_channel(session);
+ switch_time_t ts = switch_timestamp_now();
+ nibble_data_t *nibble_data;
+
+ if (!channel) {
+ 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_");
+
+ if (!nibble_data) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Can't reset - channel is not initialized for billing!\n");
+ return;
+ }
+
+ /* Lock this session's data for this module while we tinker with it */
+ if (globals.mutex) {
+ switch_mutex_lock(globals.mutex);
+ }
+
+ /* Update the last time we billed */
+ nibble_data->lastts = ts;
+
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Reset last billing timestamp marker to right now!\n");
+
+ /* Done checking - release lock */
+ if (globals.mutex) {
+ switch_mutex_unlock(globals.mutex);
+ }
+
+}
+
+static float nibblebill_check(switch_core_session_t *session)
+{
+ switch_channel_t *channel = switch_core_session_get_channel(session);
+ nibble_data_t *nibble_data;
+ float amount = 0;
+
+ if (!channel) {
+ return -99999;
+ }
+
+ /* 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_");
+
+ if (!nibble_data) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Can't check - channel is not initialized for billing!\n");
+ return -99999;
+ }
+
+ /* Lock this session's data for this module while we tinker with it */
+ if (globals.mutex) {
+ switch_mutex_lock(globals.mutex);
+ }
+
+ amount = nibble_data->total;
+
+ /* Done checking - release lock */
+ if (globals.mutex) {
+ switch_mutex_unlock(globals.mutex);
+ }
+
+ return amount;
+}
+
+static void nibblebill_adjust(switch_core_session_t *session, float amount)
+{
+ switch_channel_t *channel = switch_core_session_get_channel(session);
+
+ if (!channel) {
return;
}
@@ -538,12 +633,11 @@
/* Return if there's no billing information on this session */
if (!billaccount) {
- switch_core_session_rwunlock(session);
return;
}
/* Add or remove amount from adjusted billing here. Note, we bill the OPPOSITE */
- if (bill_event(-amount, billaccount)) {
+ if (bill_event(-amount, billaccount) == SWITCH_STATUS_SUCCESS) {
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);
@@ -562,14 +656,16 @@
&& (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")) {
+ } else if (!strcasecmp(argv[0], "flush")) {
+ do_billing(session);
+ } else if (!strcasecmp(argv[0], "pause")) {
nibblebill_pause(session);
} else if (!strcasecmp(argv[0], "resume")) {
- nibblebill_resume(session);*/
+ 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], "reset")) {
+ nibblebill_reset(session);
} else if (!strcasecmp(argv[0], "heartbeat") && argc == 2) {
switch_core_session_enable_heartbeat(session, atoi(argv[1]));
}
@@ -578,15 +674,74 @@
}
/* We get here from the API only (theoretically) */
-#define CHECK_API_SYNTAX "<uuid>"
-SWITCH_STANDARD_API(nibblebill_check_api_function)
+#define API_SYNTAX "<uuid> [pause | resume | reset | adjust <amount> | heartbeat <seconds> | check]"
+SWITCH_STANDARD_API(nibblebill_api_function)
{
- switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "We made it!\n");
-
+ switch_core_session_t *psession = NULL;
+ char *mycmd = NULL, *argv[3] = { 0 };
+ int argc = 0;
+
+ if (!switch_strlen_zero(cmd) && (mycmd = strdup(cmd))) {
+ argc = switch_separate_string(mycmd, ' ', argv, (sizeof(argv) / sizeof(argv[0])));
+ if ((argc == 2 || argc == 3) && !switch_strlen_zero(argv[0])) {
+ char *uuid = argv[0];
+ if ((psession = switch_core_session_locate(uuid))) {
+ switch_channel_t *channel;
+ channel = switch_core_session_get_channel(psession);
+
+ if (!strcasecmp(argv[1], "adjust") && argc == 3) {
+ nibblebill_adjust(psession, atof(argv[2]));
+ } else if (!strcasecmp(argv[1], "flush")) {
+ do_billing(psession);
+ } else if (!strcasecmp(argv[1], "pause")) {
+ nibblebill_pause(psession);
+ } else if (!strcasecmp(argv[1], "resume")) {
+ nibblebill_resume(psession);
+ } else if (!strcasecmp(argv[1], "check")) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Current billing is at $%f\n", nibblebill_check(psession));
+ } else if (!strcasecmp(argv[1], "reset")) {
+ nibblebill_reset(psession);
+ } else if (!strcasecmp(argv[1], "heartbeat") && argc == 3) {
+ switch_core_session_enable_heartbeat(psession, atoi(argv[2]));
+ }
+
+ switch_core_session_rwunlock(psession);
+ } else {
+ stream->write_function(stream, "-ERR No Such Channel!\n");
+ }
+ } else {
+ stream->write_function(stream, "-USAGE: %s\n", API_SYNTAX);
+ }
+ }
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
/* Check if session has variable "billrate" set. If it does, activate the heartbeat variable
switch_core_session_enable_heartbeat(switch_core_session_t *session, uint32_t seconds)
switch_core_session_sched_heartbeat(switch_core_session_t *session, uint32_t seconds)*/
+static switch_status_t sched_billing(switch_core_session_t *session)
+{
+
+ if (globals.global_heartbeat > 0) {
+ switch_core_session_enable_heartbeat(session, globals.global_heartbeat);
+ }
+
+ /* TODO: Check account balance here */
+
+ return SWITCH_STATUS_SUCCESS;
+
+}
+
+static switch_status_t process_hangup(switch_core_session_t *session)
+{
+
+ /* Resume any paused billings, just in case */
+// nibblebill_resume(session);
+
+ /* Now go handle like normal billing */
+ do_billing(session);
return SWITCH_STATUS_SUCCESS;
@@ -597,7 +752,7 @@
/* on_init */ NULL,
/* on_routing */ NULL, /* Need to add a check here for anything in their account before routing */
/* on_execute */ sched_billing, /* Turn on heartbeat for this session and do an initial account check */
- /* on_hangup */ do_billing, /* On hangup - most important place to go bill */
+ /* on_hangup */ process_hangup, /* On hangup - most important place to go bill */
/* on_exch_media */ NULL,
/* on_soft_exec */ NULL,
/* on_consume_med */ NULL,
@@ -621,18 +776,9 @@
*module_interface = switch_loadable_module_create_module_interface(pool, modname);
/* Add API and CLI commands */
-/* SWITCH_ADD_API(api_interface, "pause", "Pause billing on a session", nibblebill_pause_api_function, PAUSE_API_SYNTAX);
- SWITCH_ADD_API(api_interface, "resume", "Resume billing on a session", nibblebill_resume_api_function, RESUME_API_SYNTAX);
- SWITCH_ADD_API(api_interface, "reset", "Reset billing start time to right now - disregard any unbilled time thus far", nibblebill_reset_api_function, RESET_API_SYNTAX);
- SWITCH_ADD_API(api_interface, "adjust", "Add or deduct a dollar amount from a caller's account", nibblebill_adjust_api_function, ADJUST_API_SYNTAX);*/
- SWITCH_ADD_API(api_interface, "check", "Check the balance of a session", nibblebill_check_api_function, CHECK_API_SYNTAX);
+ SWITCH_ADD_API(api_interface, "nibblebill", "Manage billing parameters for a channel/call", nibblebill_api_function, API_SYNTAX);
/* Add dialplan applications */
-/* 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 */
More information about the Freeswitch-svn
mailing list