[commit] r11222 - freeswitch/trunk/scripts/contrib/dschreiber/mod_nibblebill
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);
participants (1)
-
FreeSWITCH SVN