[asterisk-commits] mjordan: branch mjordan/cdrs-of-doom r384407 - in /team/mjordan/cdrs-of-doom:...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Sat Mar 30 18:40:54 CDT 2013


Author: mjordan
Date: Sat Mar 30 18:40:46 2013
New Revision: 384407

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=384407
Log:
Update CDRs of doom with some dial support

This adds some mock dial stasis messages that emulate the proposed dial
messages currently up for review. CDRs handle the dial messages as a
sort of pseudo-bridge. If we go with the early bridge, this will probably
have to be re-visited again.


Modified:
    team/mjordan/cdrs-of-doom/apps/app_dial.c
    team/mjordan/cdrs-of-doom/cdr/cdr_custom.c
    team/mjordan/cdrs-of-doom/cdr/cdr_syslog.c
    team/mjordan/cdrs-of-doom/include/asterisk/cdr.h
    team/mjordan/cdrs-of-doom/include/asterisk/channel.h
    team/mjordan/cdrs-of-doom/include/asterisk/test.h
    team/mjordan/cdrs-of-doom/main/cdr.c
    team/mjordan/cdrs-of-doom/main/channel.c
    team/mjordan/cdrs-of-doom/main/channel_internal_api.c
    team/mjordan/cdrs-of-doom/main/features.c
    team/mjordan/cdrs-of-doom/main/test.c
    team/mjordan/cdrs-of-doom/tests/test_cdr.c

Modified: team/mjordan/cdrs-of-doom/apps/app_dial.c
URL: http://svnview.digium.com/svn/asterisk/team/mjordan/cdrs-of-doom/apps/app_dial.c?view=diff&rev=384407&r1=384406&r2=384407
==============================================================================
--- team/mjordan/cdrs-of-doom/apps/app_dial.c (original)
+++ team/mjordan/cdrs-of-doom/apps/app_dial.c Sat Mar 30 18:40:46 2013
@@ -710,6 +710,7 @@
 AST_LIST_HEAD_NOLOCK(dial_head, chanlist);
 
 static int detect_disconnect(struct ast_channel *chan, char code, struct ast_str **featurecode);
+static const char *hangup_cause_to_dial_status(int hangup_cause);
 
 static void chanlist_free(struct chanlist *outgoing)
 {
@@ -816,63 +817,6 @@
 	ast_channel_unlock(chan);
 
 	return ast_get_hint(NULL, 0, name, namelen, chan, context, exten) ? name : "";
-}
-
-static void senddialevent(struct ast_channel *src, struct ast_channel *dst, const char *dialstring)
-{
-	struct ast_channel *chans[] = { src, dst };
-	/*** DOCUMENTATION
-		<managerEventInstance>
-			<synopsis>Raised when a dial action has started.</synopsis>
-			<syntax>
-				<parameter name="SubEvent">
-					<para>A sub event type, specifying whether the dial action has begun or ended.</para>
-					<enumlist>
-						<enum name="Begin"/>
-						<enum name="End"/>
-					</enumlist>
-				</parameter>
-			</syntax>
-		</managerEventInstance>
-	***/
-	ast_manager_event_multichan(EVENT_FLAG_CALL, "Dial", 2, chans,
-		"SubEvent: Begin\r\n"
-		"Channel: %s\r\n"
-		"Destination: %s\r\n"
-		"CallerIDNum: %s\r\n"
-		"CallerIDName: %s\r\n"
-		"ConnectedLineNum: %s\r\n"
-		"ConnectedLineName: %s\r\n"
-		"UniqueID: %s\r\n"
-		"DestUniqueID: %s\r\n"
-		"Dialstring: %s\r\n",
-		ast_channel_name(src), ast_channel_name(dst),
-		S_COR(ast_channel_caller(src)->id.number.valid, ast_channel_caller(src)->id.number.str, "<unknown>"),
-		S_COR(ast_channel_caller(src)->id.name.valid, ast_channel_caller(src)->id.name.str, "<unknown>"),
-		S_COR(ast_channel_connected(src)->id.number.valid, ast_channel_connected(src)->id.number.str, "<unknown>"),
-		S_COR(ast_channel_connected(src)->id.name.valid, ast_channel_connected(src)->id.name.str, "<unknown>"),
-		ast_channel_uniqueid(src), ast_channel_uniqueid(dst),
-		dialstring ? dialstring : "");
-}
-
-static void senddialendevent(struct ast_channel *src, const char *dialstatus)
-{
-	/*** DOCUMENTATION
-		<managerEventInstance>
-			<synopsis>Raised when a dial action has ended.</synopsis>
-			<syntax>
-				<parameter name="DialStatus">
-					<para>The value of the <variable>DIALSTATUS</variable> channel variable.</para>
-				</parameter>
-			</syntax>
-		</managerEventInstance>
-	***/
-	ast_manager_event(src, EVENT_FLAG_CALL, "Dial",
-		"SubEvent: End\r\n"
-		"Channel: %s\r\n"
-		"UniqueID: %s\r\n"
-		"DialStatus: %s\r\n",
-		ast_channel_name(src), ast_channel_uniqueid(src), dialstatus);
 }
 
 /*!
@@ -1069,7 +1013,7 @@
 			num->nochan++;
 		} else {
 			ast_channel_lock_both(c, in);
-			senddialevent(in, c, stuff);
+			ast_channel_publish_dial(c, in, stuff, NULL);
 			ast_channel_unlock(in);
 			ast_channel_unlock(c);
 			/* Hangup the original channel now, in case we needed it */
@@ -1089,6 +1033,17 @@
 	char privintro[1024];
 	char status[256];
 };
+
+static void publish_dial_end_event(struct ast_channel *in, struct dial_head *out_chans, struct ast_channel *exception, const char *status)
+{
+	struct chanlist *outgoing;
+	AST_LIST_TRAVERSE(out_chans, outgoing, node) {
+		if (!outgoing->chan || outgoing->chan == exception) {
+			continue;
+		}
+		ast_channel_publish_dial(in, outgoing->chan, NULL, status);
+	}
+}
 
 static struct ast_channel *wait_for_answer(struct ast_channel *in,
 	struct dial_head *out_chans, int *to, struct ast_flags64 *peerflags,
@@ -1133,6 +1088,7 @@
 				*to = -1;
 				strcpy(pa->status, "CONGESTION");
 				ast_cdr_failed(ast_channel_cdr(in));
+				ast_channel_publish_dial(in, outgoing->chan, NULL, pa->status);
 				return NULL;
 			}
 		}
@@ -1293,6 +1249,7 @@
 #ifdef HAVE_EPOLL
 				ast_poll_channel_del(in, c);
 #endif
+				ast_channel_publish_dial(in, c, NULL, hangup_cause_to_dial_status(ast_channel_hangupcause(c)));
 				ast_hangup(c);
 				c = o->chan = NULL;
 				ast_clear_flag64(o, DIAL_STILLGOING);
@@ -1333,6 +1290,8 @@
 							}
 						}
 						peer = c;
+						ast_channel_publish_dial(in, peer, NULL, "ANSWER");
+						publish_dial_end_event(in, out_chans, peer, "CANCEL");
 						if (ast_channel_cdr(peer)) {
 							ast_channel_cdr(peer)->answer = ast_tvnow();
 							ast_channel_cdr(peer)->disposition = AST_CDR_ANSWERED;
@@ -1346,9 +1305,10 @@
 							DIAL_NOFORWARDHTML);
 						ast_channel_dialcontext_set(c, "");
 						ast_channel_exten_set(c, "");
-						if (CAN_EARLY_BRIDGE(peerflags, in, peer))
+						if (CAN_EARLY_BRIDGE(peerflags, in, peer)) {
 							/* Setup early bridge if appropriate */
 							ast_channel_early_bridge(in, peer);
+						}
 					}
 					/* If call has been answered, then the eventual hangup is likely to be normal hangup */
 					ast_channel_hangupcause_set(in, AST_CAUSE_NORMAL_CLEARING);
@@ -1357,6 +1317,7 @@
 				case AST_CONTROL_BUSY:
 					ast_verb(3, "%s is busy\n", ast_channel_name(c));
 					ast_channel_hangupcause_set(in, ast_channel_hangupcause(c));
+					ast_channel_publish_dial(in, c, NULL, hangup_cause_to_dial_status(ast_channel_hangupcause(c)));
 					ast_hangup(c);
 					c = o->chan = NULL;
 					ast_clear_flag64(o, DIAL_STILLGOING);
@@ -1365,6 +1326,7 @@
 				case AST_CONTROL_CONGESTION:
 					ast_verb(3, "%s is circuit-busy\n", ast_channel_name(c));
 					ast_channel_hangupcause_set(in, ast_channel_hangupcause(c));
+					ast_channel_publish_dial(in, c, NULL, hangup_cause_to_dial_status(ast_channel_hangupcause(c)));
 					ast_hangup(c);
 					c = o->chan = NULL;
 					ast_clear_flag64(o, DIAL_STILLGOING);
@@ -1572,6 +1534,7 @@
 				*to = -1;
 				strcpy(pa->status, "CANCEL");
 				ast_cdr_noanswer(ast_channel_cdr(in));
+				publish_dial_end_event(in, out_chans, NULL, pa->status);
 				if (f) {
 					if (f->data.uint32) {
 						ast_channel_hangupcause_set(in, f->data.uint32);
@@ -1596,6 +1559,7 @@
 						ast_cdr_noanswer(ast_channel_cdr(in));
 						*result = f->subclass.integer;
 						strcpy(pa->status, "CANCEL");
+						publish_dial_end_event(in, out_chans, NULL, pa->status);
 						ast_frfree(f);
 						ast_channel_unlock(in);
 						if (is_cc_recall) {
@@ -1612,6 +1576,7 @@
 					*to = 0;
 					strcpy(pa->status, "CANCEL");
 					ast_cdr_noanswer(ast_channel_cdr(in));
+					publish_dial_end_event(in, out_chans, NULL, pa->status);
 					ast_frfree(f);
 					if (is_cc_recall) {
 						ast_cc_completed(in, "CC completed, but the caller hung up with DTMF");
@@ -1707,6 +1672,7 @@
 
 	if (!*to) {
 		ast_verb(3, "Nobody picked up in %d ms\n", orig);
+		publish_dial_end_event(in, out_chans, NULL, "NOANSWER");
 	}
 	if (!*to || ast_check_hangup(in)) {
 		ast_cdr_noanswer(ast_channel_cdr(in));
@@ -1723,6 +1689,22 @@
 		ast_cc_completed(in, "Recall completed!");
 	}
 	return peer;
+}
+
+static const char *hangup_cause_to_dial_status(int hangup_cause)
+{
+	switch(hangup_cause) {
+	case AST_CAUSE_BUSY:
+		return "BUSY";
+	case AST_CAUSE_CONGESTION:
+		return "CONGESTION";
+	case AST_CAUSE_NO_ROUTE_DESTINATION:
+	case AST_CAUSE_UNREGISTERED:
+		return "CHANUNAVAIL";
+	case AST_CAUSE_NO_ANSWER:
+	default:
+		return "NOANSWER";
+	}
 }
 
 static int detect_disconnect(struct ast_channel *chan, char code, struct ast_str **featurecode)
@@ -2621,7 +2603,7 @@
 			continue;
 		}
 
-		senddialevent(chan, tmp->chan, tmp->number);
+		ast_channel_publish_dial(chan, tmp->chan, tmp->number, NULL);
 		ast_channel_unlock(chan);
 
 		ast_verb(3, "Called %s\n", tmp->interface);
@@ -3098,7 +3080,6 @@
 	ast_channel_early_bridge(chan, NULL);
 	hanguptree(&out_chans, NULL, ast_channel_hangupcause(chan)==AST_CAUSE_ANSWERED_ELSEWHERE || ast_test_flag64(&opts, OPT_CANCEL_ELSEWHERE) ? 1 : 0 ); /* forward 'answered elsewhere' if we received it */
 	pbx_builtin_setvar_helper(chan, "DIALSTATUS", pa.status);
-	senddialendevent(chan, pa.status);
 	ast_debug(1, "Exiting with DIALSTATUS=%s.\n", pa.status);
 
 	if ((ast_test_flag64(peerflags, OPT_GO_ON)) && !ast_check_hangup(chan) && (res != AST_PBX_INCOMPLETE)) {

Modified: team/mjordan/cdrs-of-doom/cdr/cdr_custom.c
URL: http://svnview.digium.com/svn/asterisk/team/mjordan/cdrs-of-doom/cdr/cdr_custom.c?view=diff&rev=384407&r1=384406&r2=384407
==============================================================================
--- team/mjordan/cdrs-of-doom/cdr/cdr_custom.c (original)
+++ team/mjordan/cdrs-of-doom/cdr/cdr_custom.c Sat Mar 30 18:40:46 2013
@@ -67,20 +67,20 @@
 
 static const char name[] = "cdr-custom";
 
-struct cdr_config {
+struct cdr_custom_config {
 	AST_DECLARE_STRING_FIELDS(
 		AST_STRING_FIELD(filename);
 		AST_STRING_FIELD(format);
 		);
 	ast_mutex_t lock;
-	AST_RWLIST_ENTRY(cdr_config) list;
+	AST_RWLIST_ENTRY(cdr_custom_config) list;
 };
 
-static AST_RWLIST_HEAD_STATIC(sinks, cdr_config);
+static AST_RWLIST_HEAD_STATIC(sinks, cdr_custom_config);
 
 static void free_config(void)
 {
-	struct cdr_config *sink;
+	struct cdr_custom_config *sink;
 	while ((sink = AST_RWLIST_REMOVE_HEAD(&sinks, list))) {
 		ast_mutex_destroy(&sink->lock);
 		ast_free(sink);
@@ -103,7 +103,7 @@
 	var = ast_variable_browse(cfg, "mappings");
 	while (var) {
 		if (!ast_strlen_zero(var->name) && !ast_strlen_zero(var->value)) {
-			struct cdr_config *sink = ast_calloc_with_stringfields(1, struct cdr_config, 1024);
+			struct cdr_custom_config *sink = ast_calloc_with_stringfields(1, struct cdr_custom_config, 1024);
 
 			if (!sink) {
 				ast_log(LOG_ERROR, "Unable to allocate memory for configuration settings.\n");
@@ -130,7 +130,7 @@
 {
 	struct ast_channel *dummy;
 	struct ast_str *str;
-	struct cdr_config *config;
+	struct cdr_custom_config *config;
 
 	/* Batching saves memory management here.  Otherwise, it's the same as doing an allocation and free each time. */
 	if (!(str = ast_str_thread_get(&custom_buf, 16))) {

Modified: team/mjordan/cdrs-of-doom/cdr/cdr_syslog.c
URL: http://svnview.digium.com/svn/asterisk/team/mjordan/cdrs-of-doom/cdr/cdr_syslog.c?view=diff&rev=384407&r1=384406&r2=384407
==============================================================================
--- team/mjordan/cdrs-of-doom/cdr/cdr_syslog.c (original)
+++ team/mjordan/cdrs-of-doom/cdr/cdr_syslog.c Sat Mar 30 18:40:46 2013
@@ -60,7 +60,7 @@
 
 static const char name[] = "cdr-syslog";
 
-struct cdr_config {
+struct cdr_syslog_config {
 	AST_DECLARE_STRING_FIELDS(
 		AST_STRING_FIELD(ident);
 		AST_STRING_FIELD(format);
@@ -68,14 +68,14 @@
 	int facility;
 	int priority;
 	ast_mutex_t lock;
-	AST_LIST_ENTRY(cdr_config) list;
+	AST_LIST_ENTRY(cdr_syslog_config) list;
 };
 
-static AST_RWLIST_HEAD_STATIC(sinks, cdr_config);
+static AST_RWLIST_HEAD_STATIC(sinks, cdr_syslog_config);
 
 static void free_config(void)
 {
-	struct cdr_config *sink;
+	struct cdr_syslog_config *sink;
 	while ((sink = AST_RWLIST_REMOVE_HEAD(&sinks, list))) {
 		ast_mutex_destroy(&sink->lock);
 		ast_free(sink);
@@ -86,7 +86,7 @@
 {
 	struct ast_channel *dummy;
 	struct ast_str *str;
-	struct cdr_config *sink;
+	struct cdr_syslog_config *sink;
 
 	/* Batching saves memory management here.  Otherwise, it's the same as doing an
 	   allocation and free each time. */
@@ -174,7 +174,7 @@
 	}
 
 	while ((catg = ast_category_browse(cfg, catg))) {
-		struct cdr_config *sink;
+		struct cdr_syslog_config *sink;
 
 		if (!strcasecmp(catg, "general")) {
 			continue;
@@ -186,7 +186,7 @@
 			continue;
 		}
 
-		sink = ast_calloc_with_stringfields(1, struct cdr_config, 1024);
+		sink = ast_calloc_with_stringfields(1, struct cdr_syslog_config, 1024);
 
 		if (!sink) {
 			ast_log(AST_LOG_ERROR,

Modified: team/mjordan/cdrs-of-doom/include/asterisk/cdr.h
URL: http://svnview.digium.com/svn/asterisk/team/mjordan/cdrs-of-doom/include/asterisk/cdr.h?view=diff&rev=384407&r1=384406&r2=384407
==============================================================================
--- team/mjordan/cdrs-of-doom/include/asterisk/cdr.h (original)
+++ team/mjordan/cdrs-of-doom/include/asterisk/cdr.h Sat Mar 30 18:40:46 2013
@@ -29,6 +29,37 @@
 #include <sys/time.h>
 
 #include "asterisk/data.h"
+
+
+enum cdr_settings {
+	CDR_ENABLED = 1 << 0,				/*< Enable CDRs */
+	CDR_BATCHMODE = 1 << 1,				/*< Whether or not we should dispatch CDRs in batches */
+	CDR_UNANSWERED = 1 << 2,			/*< Log unanswered CDRs */
+	CDR_CONGESTION = 1 << 3,			/*< Treat congestion as if it were a failed call */
+	CDR_END_BEFORE_H_EXTEN = 1 << 4,	/*< End the CDR before the 'h' extension runs */
+	CDR_INITIATED_SECONDS = 1 << 5,		/*< Include microseconds into the billing time */
+	CDR_DEBUG = 1 << 6,
+};
+
+enum batch_mode_settings {
+	BATCH_MODE_SCHEDULER_ONLY = 1 << 0,	/*< Don't spawn a thread to handle the batches - do it on the scheduler */
+	BATCH_MODE_SAFE_SHUTDOWN = 1 << 1,	/*< During safe shutdown, submit the batched CDRs */
+};
+
+/*! \brief The global options available for CDRs */
+struct cdr_config {
+	struct ast_flags settings;			/*< CDR settings */
+	struct batch_settings {
+		unsigned int time;				/*< Time between batches */
+		unsigned int size;				/*< Size to trigger a batch */
+		struct ast_flags settings;		/*< Settings for batches */
+	} batch_settings;
+};
+
+struct cdr_config *ast_cdr_get_config(void);
+
+void ast_cdr_set_config(struct cdr_config *config);
+
 
 /*!
  * \brief CDR Flags
@@ -427,6 +458,10 @@
  */
 int ast_cdr_setuserfield(struct ast_channel *chan, const char *userfield);
 /*!
+ * TODO: so this is goofy. When a channel is bridged, it gets the userfield for
+ * the peer appended to it in a semi-colon delineated way.
+ *
+ *
  * \brief Append to CDR user field for channel (stored in CDR)
  * \note The channel should be locked before calling.
  */
@@ -434,16 +469,16 @@
 
 
 /*!
+ * TODO: this should happen automatically via a snapshot call
  * \brief Update CDR on a channel
  * \note The channel should be locked before calling.
  */
 int ast_cdr_update(struct ast_channel *chan);
 
-
+/* TODO: This just needs to be a function call */
 extern int ast_default_amaflags;
 
-extern char ast_default_accountcode[AST_MAX_ACCOUNT_CODE];
-
+/* TODO: this gets replaced either by snapshots or by fork CDR sending a message */
 struct ast_cdr *ast_cdr_append(struct ast_cdr *cdr, struct ast_cdr *newcdr);
 
 /*! \brief Reload the configuration file cdr.conf and start/stop CDR scheduling thread */
@@ -455,14 +490,6 @@
 /*! Submit any remaining CDRs and prepare for shutdown */
 void ast_cdr_engine_term(void);
 
-/*!
- * \brief
- * \param[in] tree Where to insert the cdr.
- * \param[in] cdr The cdr structure to insert in 'tree'.
- * \param[in] recur Go throw all the cdr levels.
- * \retval <0 on error.
- * \retval 0 on success.
- */
-int ast_cdr_data_add_structure(struct ast_data *tree, struct ast_cdr *cdr, int recur);
+
 
 #endif /* _ASTERISK_CDR_H */

Modified: team/mjordan/cdrs-of-doom/include/asterisk/channel.h
URL: http://svnview.digium.com/svn/asterisk/team/mjordan/cdrs-of-doom/include/asterisk/channel.h?view=diff&rev=384407&r1=384406&r2=384407
==============================================================================
--- team/mjordan/cdrs-of-doom/include/asterisk/channel.h (original)
+++ team/mjordan/cdrs-of-doom/include/asterisk/channel.h Sat Mar 30 18:40:46 2013
@@ -365,7 +365,7 @@
  * PSTN gateway).
  *
  * \todo Implement settings for transliteration between UTF8 Caller ID names in
- *       to ASCII Caller ID's (DAHDI). Östen Åsklund might be transliterated into
+ *       to ASCII Caller ID's (DAHDI). �sten �sklund might be transliterated into
  *       Osten Asklund or Oesten Aasklund depending upon language and person...
  *       We need automatic routines for incoming calls and static settings for
  *       our own accounts.
@@ -917,6 +917,8 @@
 	 * to continue.
 	 */
 	AST_FLAG_BRIDGE_DUAL_REDIRECT_WAIT = (1 << 22),
+
+	AST_FLAG_ORIGINATED = (1 << 23),
 };
 
 /*! \brief ast_bridge_config flags */
@@ -4228,7 +4230,25 @@
 };
 
 /*!
+ * \brief A channel dial message payload
  * \since 12
+ */
+struct ast_channel_dial {
+	/*! The channel performing the dial operation */
+	struct ast_channel_snapshot *caller;
+	/*! The channel being dialed */
+	struct ast_channel_snapshot *peer;
+	AST_DECLARE_STRING_FIELDS(
+		/*! The result of the dial operation. */
+		AST_STRING_FIELD(dialstatus);
+		/*! The information passed to the application/framework dialing */
+		AST_STRING_FIELD(dialstring);
+	);
+};
+
+/*!
+ * \since 12
+ *
  * \brief Message type for \ref ast_channel_blob messages.
  *
  * \retval Message type for \ref ast_channel_blob messages.
@@ -4272,4 +4292,29 @@
 void ast_channel_publish_varset(struct ast_channel *chan,
 				const char *variable, const char *value);
 
+/*!
+ * \brief Message type for \ref ast_channel_dial messages.
+ *
+ * \retval Message type for \ref ast_channel_dial messages.
+ *
+ * \since 12
+ */
+struct stasis_message_type *ast_channel_dial_message(void);
+
+/*!
+ * \brief Publish a \ref ast_channel_dial message for the channels involved
+ * in a dial operations
+ *
+ * \param caller The channel performing the dial operation
+ * \param peer The channel being dialed
+ * \param dialstring When beginning a dial, the information passed to the dialing application
+ * \param dialstatus The current status of the dial operation (NULL if no status is known)
+ *
+ * \since 12
+ */
+void ast_channel_publish_dial(struct ast_channel *caller,
+		struct ast_channel *peer,
+		const char *dialstring,
+		const char *dialstatus);
+
 #endif /* _ASTERISK_CHANNEL_H */

Modified: team/mjordan/cdrs-of-doom/include/asterisk/test.h
URL: http://svnview.digium.com/svn/asterisk/team/mjordan/cdrs-of-doom/include/asterisk/test.h?view=diff&rev=384407&r1=384406&r2=384407
==============================================================================
--- team/mjordan/cdrs-of-doom/include/asterisk/test.h (original)
+++ team/mjordan/cdrs-of-doom/include/asterisk/test.h Sat Mar 30 18:40:46 2013
@@ -228,7 +228,9 @@
 /*!
  * \brief Generic test callback function
  *
- * \param error buffer string for failure results
+ * \param info The test info object
+ * \param cmd What to perform in the test
+ * \param test The actual test object being manipulated
  *
  * \retval AST_TEST_PASS for pass
  * \retval AST_TEST_FAIL for failure
@@ -236,6 +238,10 @@
 typedef enum ast_test_result_state (ast_test_cb_t)(struct ast_test_info *info,
 	enum ast_test_command cmd, struct ast_test *test);
 
+typedef int (ast_test_init_cb_t)(struct ast_test_info *info, struct ast_test *test);
+
+typedef int (ast_test_cleanup_cb_t)(struct ast_test_info *info, struct ast_test *test);
+
 /*!
  * \brief unregisters a test with the test framework
  *
@@ -256,6 +262,11 @@
  */
 int ast_test_register(ast_test_cb_t *cb);
 
+int ast_test_register_init(const char *category, ast_test_init_cb_t *cb);
+
+int ast_test_register_cleanup(const char *category, ast_test_cleanup_cb_t *cb);
+
+
 /*!
  * \brief Unit test debug output.
  * \since 12.0.0
@@ -267,6 +278,9 @@
  */
 void ast_test_debug(struct ast_test *test, const char *fmt, ...) __attribute__((format(printf, 2, 3)));
 
+/*!
+ * \brief Set the result of a test
+ */
 void ast_test_set_result(struct ast_test *test, enum ast_test_result_state state);
 
 

Modified: team/mjordan/cdrs-of-doom/main/cdr.c
URL: http://svnview.digium.com/svn/asterisk/team/mjordan/cdrs-of-doom/main/cdr.c?view=diff&rev=384407&r1=384406&r2=384407
==============================================================================
--- team/mjordan/cdrs-of-doom/main/cdr.c (original)
+++ team/mjordan/cdrs-of-doom/main/cdr.c Sat Mar 30 18:40:46 2013
@@ -64,6 +64,7 @@
 #include "asterisk/data.h"
 #include "asterisk/config_options.h"
 #include "asterisk/stasis.h"
+#include "asterisk/stasis_message_router.h"
 #include "asterisk/astobj2.h"
 
 /*** DOCUMENTATION
@@ -192,29 +193,12 @@
 /*! Default AMA flag for billing records (CDR's) */
 int ast_default_amaflags = DEFAULT_AMA_FLAGS;
 
-enum cdr_settings {
-	CDR_ENABLED = 1 << 0,				/*< Enable CDRs */
-	CDR_BATCHMODE = 1 << 1,				/*< Whether or not we should dispatch CDRs in batches */
-	CDR_UNANSWERED = 1 << 2,			/*< Log unanswered CDRs */
-	CDR_CONGESTION = 1 << 3,			/*< Treat congestion as if it were a failed call */
-	CDR_END_BEFORE_H_EXTEN = 1 << 4,	/*< End the CDR before the 'h' extension runs */
-	CDR_INITIATED_SECONDS = 1 << 5,		/*< Include microseconds into the billing time */
-};
-
-enum batch_mode_settings {
-	BATCH_MODE_SCHEDULER_ONLY = 1 << 0,	/*< Don't spawn a thread to handle the batches - do it on the scheduler */
-	BATCH_MODE_SAFE_SHUTDOWN = 1 << 1,	/*< During safe shutdown, submit the batched CDRs */
-};
-
-/*! \brief The global options available for CDRs */
-struct cdr_config {
-	struct ast_flags settings;			/*< CDR settings */
-	struct batch_settings {
-		unsigned int time;				/*< Time between batches */
-		unsigned int size;				/*< Size to trigger a batch */
-		struct ast_flags settings;		/*< Settings for batches */
-	} batch_settings;
-};
+#define CDR_DEBUG(mod_cfg, fmt, ...) \
+	do { \
+	if (ast_test_flag(&(mod_cfg)->general->settings, CDR_DEBUG)) { \
+		ast_verb(1, (fmt), ##__VA_ARGS__); \
+	} } while (0)
+
 
 /*! \brief The configuration settings for this module */
 struct module_config {
@@ -318,37 +302,52 @@
 
 
 
-
-
-
 static struct ao2_container *active_cdrs_by_channel;
 
-static struct stasis_subscription *channel_state_sub;
+static struct stasis_message_router *stasis_router;
 
 struct cdr_object;
 
 struct cdr_object_fn_table {
+	const char *name;
 	void (* const init_function)(struct cdr_object *cdr);
 	void (* const process_channel_message)(struct cdr_object *cdr, struct ast_channel_snapshot *snapshot);
 	void (* const process_channel_varset_message)(struct cdr_object *cdr, struct ast_channel_snapshot *snapshot, const char *name, const char *value);
+	void (* const process_dial_message)(struct cdr_object *cdr, struct ast_channel_dial *dial_message);
 };
 
 static void single_state_init_function(struct cdr_object *cdr);
 static void single_state_process_channel_message(struct cdr_object *cdr, struct ast_channel_snapshot *snapshot);
 static void single_state_process_channel_varset_message(struct cdr_object *cdr, struct ast_channel_snapshot *snapshot, const char *name, const char *value);
+static void single_state_process_dial_message(struct cdr_object *cdr, struct ast_channel_dial *dial_message);
 
 struct cdr_object_fn_table single_state_fn_table = {
+	.name = "Single",
 	.init_function = single_state_init_function,
 	.process_channel_message = single_state_process_channel_message,
 	.process_channel_varset_message = single_state_process_channel_varset_message,
+	.process_dial_message = single_state_process_dial_message,
 };
 
+static void dial_state_init_function(struct cdr_object *cdr);
+static void dial_state_process_channel_message(struct cdr_object *cdr, struct ast_channel_snapshot *snapshot);
+static void dial_state_process_dial_message(struct cdr_object *cdr, struct ast_channel_dial *dial_message);
+
+struct cdr_object_fn_table dial_state_fn_table = {
+	.name = "Dial",
+	.init_function = dial_state_init_function,
+	.process_channel_message = dial_state_process_channel_message,
+	.process_dial_message = dial_state_process_dial_message,
+};
+
 static void hangup_state_init_function(struct cdr_object *cdr);
 
 struct cdr_object_fn_table hangup_state_fn_table = {
+	.name = "Hangup",
 	.init_function = hangup_state_init_function,
 	.process_channel_message = NULL,
 	.process_channel_varset_message = NULL,
+	.process_dial_message = NULL,
 };
 
 enum cdr_object_timestamps {
@@ -438,6 +437,20 @@
 	transition_state(cdr, &single_state_fn_table);
 
 	return cdr;
+}
+
+static struct cdr_object *create_and_append_cdr(struct cdr_object *cdr)
+{
+	struct cdr_object *new_cdr;
+
+	new_cdr = cdr_object_alloc(cdr->party_a);
+	if (!new_cdr) {
+		return NULL;
+	}
+
+	/* TODO copy the variables over */
+	cdr->next = new_cdr;
+	return new_cdr;
 }
 
 static struct ast_cdr *create_public_cdr_records(struct cdr_object *cdr)
@@ -467,7 +480,8 @@
 		ast_copy_string(cdr_copy->dst, cdr->party_a->exten, sizeof(cdr_copy->dst));
 		ast_copy_string(cdr_copy->dcontext, cdr->party_a->context, sizeof(cdr_copy->dcontext));
 		ast_copy_string(cdr_copy->userfield, cdr->party_a->userfield, sizeof(cdr_copy->userfield));
-
+		/* XXX TODO: this should just happen automatically */
+		cdr_copy->flags = cdr->party_a->flags.flags;
 
 		/* Party B */
 		if (cdr->party_b) {
@@ -519,85 +533,26 @@
 	return pub_cdr;
 }
 
-static void dispatch_cdr_record(struct cdr_object *cdr)
-{
+static void dispatch_cdr_object(struct cdr_object *cdr)
+{
+	RAII_VAR(struct module_config *, mod_cfg, ao2_global_obj_ref(module_configs), ao2_cleanup);
 	struct ast_cdr *pub_cdr;
 
-	/* We shouldn't be dispatching unless party A is hung up */
-	ast_assert(cdr->fn_table == &hangup_state_fn_table);
-
+	CDR_DEBUG(mod_cfg, "%p - Dispatching CDR for Party A %s\n", cdr, cdr->party_a->name);
 	pub_cdr = create_public_cdr_records(cdr);
 	ast_cdr_detach(pub_cdr);
-
-
-}
-
-static void transition_state(struct cdr_object *cdr, struct cdr_object_fn_table *fn_table)
-{
-	cdr->fn_table = fn_table;
-	if (cdr->fn_table->init_function) {
-		cdr->fn_table->init_function(cdr);
-	}
-}
-
-static void check_for_hangup(struct cdr_object *cdr)
-{
-	if (ast_test_flag(&cdr->party_a->flags, AST_FLAG_ZOMBIE)) {
-		transition_state(cdr, &hangup_state_fn_table);
-	}
-}
-
-/* SINGLE STATE */
-
-static void single_state_init_function(struct cdr_object *cdr) {
-	cdr->timestamps[CDR_OBJECT_TIME_CREATED] = ast_tvnow();
-	if (cdr->party_a && cdr->party_a->state == AST_STATE_UP) {
-		cdr->timestamps[CDR_OBJECT_TIME_ANSWERED] = cdr->timestamps[CDR_OBJECT_TIME_CREATED];
-	}
-}
-
-static void single_state_process_channel_message(struct cdr_object *cdr, struct ast_channel_snapshot *snapshot)
-{
-	ast_assert(cdr != NULL);
-	ast_assert(snapshot != NULL);
-
-	if (strcmp(snapshot->name, cdr->party_a->name)) {
-		return;
-	}
-
-	if (snapshot->state == AST_STATE_UP && ast_tvzero(cdr->timestamps[CDR_OBJECT_TIME_ANSWERED])) {
-		cdr->timestamps[CDR_OBJECT_TIME_ANSWERED] = ast_tvnow();
-	}
-
-	ao2_ref(cdr->party_a, -1);
-	cdr->party_a = snapshot;
-	ao2_ref(cdr->party_a, +1);
-	check_for_hangup(cdr);
-}
-
-static void single_state_process_channel_varset_message(struct cdr_object *cdr, struct ast_channel_snapshot *snapshot, const char *name, const char *value)
-{
-	return;
-}
-
-/* HANGUP STATE */
-
-static void hangup_state_init_function(struct cdr_object *cdr) {
+}
+
+static void finalize_cdr_object(struct cdr_object *cdr)
+{
 	RAII_VAR(struct module_config *, mod_cfg, ao2_global_obj_ref(module_configs), ao2_cleanup);
 
-	if (ast_test_flag(&mod_cfg->general->settings, CDR_END_BEFORE_H_EXTEN)) {
-		/* DO something? */
-		/* So here's a thought. We should always finalize, and this flag should
-		 * just go away.
-		 */
-	}
-
-	/* Set the end time if it isn't yet set */
+	/* If the end time is set, we've been finalized already */
 	if (ast_tvzero(cdr->timestamps[CDR_OBJECT_TIME_FINALIZED])) {
 		cdr->timestamps[CDR_OBJECT_TIME_FINALIZED] = ast_tvnow();
 	}
 
-	if (!ast_tvzero(cdr->timestamps[CDR_OBJECT_TIME_ANSWERED])) {
+	if (!ast_tvzero(cdr->timestamps[CDR_OBJECT_TIME_ANSWERED]) && cdr->disposition == AST_CDR_NOANSWER) {
 		cdr->disposition = AST_CDR_ANSWERED;
 	}
 
@@ -620,68 +575,344 @@
 		break;
 	}
 
-	dispatch_cdr_record(cdr);
-}
-
-
-
-
-/* TOPIC CALLBACKS */
-
-static void channel_topic_callback(void *data, struct stasis_subscription *sub, struct stasis_topic *topic, struct stasis_message *message)
-{
+	CDR_DEBUG(mod_cfg, "%p - finalized CDR - start %ld.%ld answer %ld.%ld end %ld.%ld dispo %d\n",
+			cdr,
+			cdr->timestamps[CDR_OBJECT_TIME_CREATED].tv_sec,
+			cdr->timestamps[CDR_OBJECT_TIME_CREATED].tv_usec,
+			cdr->timestamps[CDR_OBJECT_TIME_ANSWERED].tv_sec,
+			cdr->timestamps[CDR_OBJECT_TIME_ANSWERED].tv_usec,
+			cdr->timestamps[CDR_OBJECT_TIME_FINALIZED].tv_sec,
+			cdr->timestamps[CDR_OBJECT_TIME_FINALIZED].tv_usec,
+			cdr->disposition);
+}
+
+static void transition_state(struct cdr_object *cdr, struct cdr_object_fn_table *fn_table)
+{
+	RAII_VAR(struct module_config *, mod_cfg, ao2_global_obj_ref(module_configs), ao2_cleanup);
+
+	CDR_DEBUG(mod_cfg, "%p - Transitioning CDR for %s from state %s to %s\n",
+		cdr, cdr->party_a->name, cdr->fn_table ? cdr->fn_table->name : "NONE", fn_table->name);
+	cdr->fn_table = fn_table;
+	if (cdr->fn_table->init_function) {
+		cdr->fn_table->init_function(cdr);
+	}
+}
+
+static void check_for_hangup(struct cdr_object *cdr)
+{
+	if (ast_test_flag(&cdr->party_a->flags, AST_FLAG_ZOMBIE)) {
+		transition_state(cdr, &hangup_state_fn_table);
+	}
+}
+
+static void check_for_answer(struct cdr_object *cdr) {
+	RAII_VAR(struct module_config *, mod_cfg, ao2_global_obj_ref(module_configs), ao2_cleanup);
+
+	if (cdr->party_a->state == AST_STATE_UP && ast_tvzero(cdr->timestamps[CDR_OBJECT_TIME_ANSWERED])) {
+		cdr->timestamps[CDR_OBJECT_TIME_ANSWERED] = ast_tvnow();
+		CDR_DEBUG(mod_cfg, "%p - Set answered time to %ld.%ld\n", cdr,
+			cdr->timestamps[CDR_OBJECT_TIME_ANSWERED].tv_sec,
+			cdr->timestamps[CDR_OBJECT_TIME_ANSWERED].tv_usec);
+	}
+}
+
+static struct ast_channel_snapshot *swap_channel_snapshot(struct ast_channel_snapshot *old, struct ast_channel_snapshot *new)
+{
+	if (old) {
+		ao2_t_ref(old, -1, "Drop ref for swap");
+	}
+	ao2_t_ref(new, +1, "Bump ref for swap");
+	return new;
+}
+
+/* SINGLE STATE */
+
+static void single_state_init_function(struct cdr_object *cdr) {
+	cdr->timestamps[CDR_OBJECT_TIME_CREATED] = ast_tvnow();
+	check_for_answer(cdr);
+}
+
+static void single_state_process_channel_message(struct cdr_object *cdr, struct ast_channel_snapshot *snapshot)
+{
+	RAII_VAR(struct module_config *, mod_cfg, ao2_global_obj_ref(module_configs), ao2_cleanup);
+
+	ast_assert(cdr != NULL);
+	ast_assert(snapshot != NULL);
+
+	if (strcmp(snapshot->name, cdr->party_a->name)) {
+		return;
+	}
+
+	cdr->party_a = swap_channel_snapshot(cdr->party_a, snapshot);
+	CDR_DEBUG(mod_cfg, "%p - Updated Party A %s snapshot\n", cdr, cdr->party_a->name);
+
+	check_for_answer(cdr);
+	check_for_hangup(cdr);
+}
+
+static void single_state_process_channel_varset_message(struct cdr_object *cdr, struct ast_channel_snapshot *snapshot, const char *name, const char *value)
+{
+	return;
+}
+
+static void single_state_process_dial_message(struct cdr_object *cdr, struct ast_channel_dial *message)
+{
+	RAII_VAR(struct module_config *, mod_cfg, ao2_global_obj_ref(module_configs), ao2_cleanup);
+	struct ast_channel_snapshot *caller = message->caller;
+	struct ast_channel_snapshot *peer = message->peer;
+
+	ast_assert(ast_strlen_zero(message->dialstatus));
+	ast_assert(!ast_strlen_zero(message->dialstring));
+
+	if (caller && !strcmp(cdr->party_a->name, caller->name)) {
+		cdr->party_a = swap_channel_snapshot(cdr->party_a, caller);
+		CDR_DEBUG(mod_cfg, "%p - Updated Party A %s snapshot\n", cdr, cdr->party_a->name);
+		cdr->party_b = swap_channel_snapshot(cdr->party_b, peer);
+		CDR_DEBUG(mod_cfg, "%p - Updated Party B %s snapshot\n", cdr, cdr->party_b->name);
+	} else if (!strcmp(cdr->party_a->name, peer->name)) {
+		/* We're the entity being dialed, i.e., outbound origination */
+		cdr->party_a = swap_channel_snapshot(cdr->party_a, peer);
+		CDR_DEBUG(mod_cfg, "%p - Updated Party A %s snapshot\n", cdr, cdr->party_a->name);
+	}
+
+	transition_state(cdr, &dial_state_fn_table);
+}
+
+/* DIAL STATE */
+
+static void dial_state_init_function(struct cdr_object *cdr)
+{
+	return;
+}
+
+static void dial_state_process_channel_message(struct cdr_object *cdr, struct ast_channel_snapshot *snapshot)
+{
+	ast_assert(snapshot != NULL);
+
+	if (!strcmp(cdr->party_a->name, snapshot->name)) {
+		cdr->party_a = swap_channel_snapshot(cdr->party_a, snapshot);
+	} else if (cdr->party_b && !strcmp(cdr->party_b->name, snapshot->name)) {
+		cdr->party_b = swap_channel_snapshot(cdr->party_b, snapshot);
+	} else {
+		ast_log(AST_LOG_WARNING, "WTF\n");
+		ast_assert(0);
+	}
+
+	check_for_answer(cdr);
+	check_for_hangup(cdr);
+}
+
+static void dial_state_process_dial_message(struct cdr_object *cdr, struct ast_channel_dial *dial_message)
+{
+	struct ast_channel_snapshot *caller = dial_message->caller;
+	struct ast_channel_snapshot *peer = dial_message->peer;
+	struct cdr_object *new_cdr;
+
+	ast_assert(!strcmp(cdr->party_a->name, caller->name));
+	cdr->party_a = swap_channel_snapshot(cdr->party_a, caller);
+
+	if (!ast_strlen_zero(dial_message->dialstatus)) {
+		if (strcmp(cdr->party_b->name, peer->name)) {
+			/* Not our status, defer to the next CDR in the chain */
+			ast_assert(cdr->next != NULL);
+			ast_assert(cdr->next->fn_table->process_dial_message != NULL);
+			cdr->next->fn_table->process_dial_message(cdr->next, dial_message);
+			return;
+		}
+
+		cdr->party_b = swap_channel_snapshot(cdr->party_b, peer);
+		/* Set the party A hangup cause based on the dial string. A subsequent hangup
+		 * message will update this if needed. This will at least let us set the CDR
+		 * disposition properly.
+		 */
+		if (!strcmp(dial_message->dialstatus, "BUSY")) {
+			cdr->party_a->hangupcause = AST_CAUSE_BUSY;
+			finalize_cdr_object(cdr);
+		} else if (!strcmp(dial_message->dialstatus, "CANCEL")) {
+			cdr->party_a->hangupcause = AST_CAUSE_NORMAL;
+			finalize_cdr_object(cdr);
+		} else if (!strcmp(dial_message->dialstatus, "CONGESTION")) {
+			cdr->party_a->hangupcause = AST_CAUSE_CONGESTION;
+			finalize_cdr_object(cdr);
+		} else if (!strcmp(dial_message->dialstatus, "FAILED")) {
+			cdr->party_a->hangupcause = AST_CAUSE_NO_ROUTE_DESTINATION;
+			finalize_cdr_object(cdr);
+		}
+		/* TODO: if ANSWER, do NOTHING? */
+	}
+	if (!ast_strlen_zero(dial_message->dialstring)) {
+		struct cdr_object *it_cdr = cdr;
+		/* If we're in a dial and we get a new dial, then ... yay! new CDR. */
+		/* No new dials if we aren't dialing someone ... */
+		ast_assert(peer != NULL);
+		while (it_cdr->next != NULL) {
+			it_cdr = it_cdr->next;
+		}
+		new_cdr = create_and_append_cdr(it_cdr);
+		new_cdr->party_b = swap_channel_snapshot(new_cdr->party_b, peer);
+		transition_state(new_cdr, &dial_state_fn_table);
+	}
+}
+
+
+/* HANGUP STATE */
+
+static void hangup_state_init_function(struct cdr_object *cdr) {
+	RAII_VAR(struct module_config *, mod_cfg, ao2_global_obj_ref(module_configs), ao2_cleanup);
+
+	if (ast_test_flag(&mod_cfg->general->settings, CDR_END_BEFORE_H_EXTEN)) {
+		finalize_cdr_object(cdr);
+	}
+}
+
+static struct cdr_object *find_cdr_by_channel(struct ast_channel_snapshot *snapshot)
+{
+	struct cdr_object *cdr;
+
+	cdr = ao2_find(active_cdrs_by_channel, snapshot->name, OBJ_KEY);
+	return cdr;
+}
+
+static struct cdr_object *find_or_create_cdr_by_channel(struct ast_channel_snapshot *snapshot)
+{
+	struct cdr_object *cdr;
+
+	cdr = find_cdr_by_channel(snapshot);
+	if (cdr) {
+		return cdr;
+	}
+
+	cdr = cdr_object_alloc(snapshot);
+	if (!cdr) {
+		return NULL;
+	}
+	ao2_link(active_cdrs_by_channel, cdr);
+
+	return cdr;
+}
+
+static struct ast_channel_snapshot *determine_party_a(struct ast_channel_snapshot *left, struct ast_channel_snapshot *right)
+{
+	/* TODO: check party a flag */
+
+	if (left->creationtime.tv_sec < right->creationtime.tv_sec) {
+		return left;
+	} else if (left->creationtime.tv_sec > right->creationtime.tv_sec) {
+		return right;
+	} else {
+		if (left->creationtime.tv_usec > right->creationtime.tv_usec) {
+			return right;
+		}
+		return left;
+	}
+}
+
+/* TOPIC ROUTER CALLBACKS */
+static void handle_dial_message(void *data, struct stasis_subscription *sub, struct stasis_topic *topic, struct stasis_message *message)
+{
+	RAII_VAR(struct module_config *, mod_cfg, ao2_global_obj_ref(module_configs), ao2_cleanup);
 	RAII_VAR(struct cdr_object *, cdr, NULL, ao2_cleanup);
-
-	/* XXX NOTE: let's get some handlers for these message types at some point */
-	if (stasis_message_type(message) == stasis_cache_update()) {
-		struct stasis_cache_update *update = stasis_message_data(message);
-		if (ast_channel_snapshot() == update->type) {
-			struct ast_channel_snapshot *old_snapshot =
-				stasis_message_data(update->old_snapshot);
-			struct ast_channel_snapshot *new_snapshot =
-				stasis_message_data(update->new_snapshot);
-			const char *name = new_snapshot ? new_snapshot->name : old_snapshot->name;
-
+	struct ast_channel_dial *payload = stasis_message_data(message);
+	struct ast_channel_snapshot *party_a;
+	struct ast_channel_snapshot *party_b;
+
+	ast_assert(payload != NULL);
+
+	if (payload->caller && payload->peer) {
+		party_a = determine_party_a(payload->caller, payload->peer);
+	} else if (payload->caller && !payload->peer) {
+		party_a = payload->caller;
+	} else if (!payload->caller && payload->peer) {
+		party_a = payload->peer;
+	} else {
+		ast_assert(0);
+		return;
+	}
+	party_b = (party_a == payload->caller) ? payload->peer : payload->caller;
+
+	cdr = find_or_create_cdr_by_channel(party_a);
+	if (!cdr) {
+		ast_assert(0);
+		return;
+	}
+	CDR_DEBUG(mod_cfg, "%p - processing dial message for channel %s, peer %s\n",
+			cdr, party_a->name, party_b ? party_b->name : "(none)");
+	if (cdr->fn_table->process_dial_message) {
+		cdr->fn_table->process_dial_message(cdr, payload);
+	}
+}
+
+static void handle_cache_update_message(void *data, struct stasis_subscription *sub, struct stasis_topic *topic, struct stasis_message *message)
+{
+	RAII_VAR(struct cdr_object *, cdr, NULL, ao2_cleanup);
+	RAII_VAR(struct module_config *, mod_cfg, ao2_global_obj_ref(module_configs), ao2_cleanup);
+	struct stasis_cache_update *update = stasis_message_data(message);
+
+	ast_assert(update != NULL);
+	if (ast_channel_snapshot() == update->type) {
+		struct ast_channel_snapshot *old_snapshot =
+			stasis_message_data(update->old_snapshot);
+		struct ast_channel_snapshot *new_snapshot =
+			stasis_message_data(update->new_snapshot);
+		const char *name = new_snapshot ? new_snapshot->name : old_snapshot->name;
+
+		if (new_snapshot && !old_snapshot) {
+			cdr = cdr_object_alloc(new_snapshot);
+			if (!cdr) {
+				return;
+			}
+			CDR_DEBUG(mod_cfg, "%p - created CDR for channel %s\n", cdr, name);
+			ao2_link(active_cdrs_by_channel, cdr);
+		}
+
+		if (!cdr) {
 			cdr = ao2_find(active_cdrs_by_channel, name, OBJ_KEY);
 			if (!cdr) {
-				if (!new_snapshot) {
-					return;
-				}
-				cdr = cdr_object_alloc(new_snapshot);
-				if (!cdr) {
-					return;
-				}
-				ao2_link(active_cdrs_by_channel, cdr);
-			}
-
-			if (!new_snapshot && cdr) {
-				ao2_unlink(active_cdrs_by_channel, cdr);
-				/* NOTE: THIS IS WHEN WE SHOULD DISPATCH!!!! */
+				ast_log(AST_LOG_WARNING, "Unable to find CDR for channel %s\n", name);
 				return;
 			}
-
-			if (cdr->fn_table->process_channel_message) {
-				cdr->fn_table->process_channel_message(cdr, new_snapshot);
-			} else {

[... 1541 lines stripped ...]



More information about the asterisk-commits mailing list