[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