[asterisk-commits] kmoore: branch kmoore/channel-state-caching r380840 - in /team/kmoore/channel...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Mon Feb 4 20:27:23 CST 2013


Author: kmoore
Date: Mon Feb  4 20:27:19 2013
New Revision: 380840

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=380840
Log:
Initial implementation

This change removes the Newchannel, Newstate, and Hangup events from
channel.c and replaces them with channel state queue function calls.
They now reside in manager.c behind an event subscription to channel
state snapshots.  This also adds the event and IE types to support
pushing channel states to consumers.

Modified:
    team/kmoore/channel-state-caching/include/asterisk/channel.h
    team/kmoore/channel-state-caching/include/asterisk/event.h
    team/kmoore/channel-state-caching/include/asterisk/event_defs.h
    team/kmoore/channel-state-caching/main/channel.c
    team/kmoore/channel-state-caching/main/event.c
    team/kmoore/channel-state-caching/main/manager.c

Modified: team/kmoore/channel-state-caching/include/asterisk/channel.h
URL: http://svnview.digium.com/svn/asterisk/team/kmoore/channel-state-caching/include/asterisk/channel.h?view=diff&rev=380840&r1=380839&r2=380840
==============================================================================
--- team/kmoore/channel-state-caching/include/asterisk/channel.h (original)
+++ team/kmoore/channel-state-caching/include/asterisk/channel.h Mon Feb  4 20:27:19 2013
@@ -151,6 +151,7 @@
 #include "asterisk/channelstate.h"
 #include "asterisk/ccss.h"
 #include "asterisk/framehook.h"
+#include "asterisk/indications.h"
 
 #define DATASTORE_INHERIT_FOREVER	INT_MAX
 
@@ -4102,4 +4103,63 @@
 void ast_channel_dialed_causes_clear(const struct ast_channel *chan);
 
 struct ast_flags *ast_channel_flags(struct ast_channel *chan);
+
+#define AST_MAX_ID	32
+#define AST_MAX_GENERIC	256
+#define AST_MAX_PARTY	64
+#ifndef AST_MAX_APP
+#define AST_MAX_APP	32
+#endif
+/*!
+ * \since 12
+ * \brief Structure representing a snapshot of channel state.
+ */
+struct ast_channel_snapshot {
+	char name[AST_CHANNEL_NAME];		/*!< ASCII unique channel name */
+	char accountcode[AST_MAX_ACCOUNT_CODE];	/*!< Account code for billing */
+	char peeraccount[AST_MAX_ACCOUNT_CODE];	/*!< Peer account code for billing */
+	char userfield[AST_MAX_USER_FIELD];	/*!< Userfield for CEL billing */
+	char uniqueid[AST_MAX_ID];		/*!< Unique Channel Identifier */
+	char linkedid[AST_MAX_ID];		/*!< Linked Channel Identifier -- gets propagated by linkage */
+	char parkinglot[AST_MAX_GENERIC];	/*! Default parking lot, if empty, default parking lot  */
+	char hangupsource[AST_MAX_GENERIC];	/*! Who is responsible for hanging up this channel */
+	char appl[AST_MAX_APP];			/*!< Current application */
+	char data[AST_MAX_GENERIC];		/*!< Data passed to current application */
+	char context[AST_MAX_CONTEXT];		/*!< Dialplan: Current extension context */
+	char exten[AST_MAX_EXTENSION];		/*!< Dialplan: Current extension number */
+
+	/*!
+	 * \brief Channel Caller ID information.
+	 * \note The caller id information is the caller id of this
+	 * channel when it is used to initiate a call.
+	 */
+	char caller_name[AST_MAX_PARTY];
+	char caller_number[AST_MAX_PARTY];
+
+	/*!
+	 * \brief Channel Connected Line ID information.
+	 * \note The connected line information identifies the channel
+	 * connected/bridged to this channel.
+	 */
+	char connected_name[AST_MAX_PARTY];
+	char connected_number[AST_MAX_PARTY];
+
+	struct timeval creationtime;	/*!< The time of channel creation */
+	enum ast_channel_state state;	/*!< State of line */
+	int priority;			/*!< Dialplan: Current extension priority */
+	int amaflags;			/*!< AMA flags for billing */
+	int hangupcause;		/*!< Why is the channel hanged up. See causes.h */
+	struct ast_flags flags;		/*!< channel flags of AST_FLAG_ type */
+};
+
+/*!
+ * \since 12
+ * \brief Generate a snapshot of the channel state
+ *
+ * \param chan The channel from which to generate a snapshot
+ *
+ * \retval pointer on success (must be ast_freed)
+ * \retval NULL on error
+ */
+struct ast_channel_snapshot *ast_channel_snapshot_create(struct ast_channel *chan);
 #endif /* _ASTERISK_CHANNEL_H */

Modified: team/kmoore/channel-state-caching/include/asterisk/event.h
URL: http://svnview.digium.com/svn/asterisk/team/kmoore/channel-state-caching/include/asterisk/event.h?view=diff&rev=380840&r1=380839&r2=380840
==============================================================================
--- team/kmoore/channel-state-caching/include/asterisk/event.h (original)
+++ team/kmoore/channel-state-caching/include/asterisk/event.h Mon Feb  4 20:27:19 2013
@@ -750,6 +750,16 @@
  */
 size_t ast_event_minimum_length(void);
 
+/*!
+ * \brief Send a channel state-related event.
+ *
+ * \param chan the channel about which to send information
+ *
+ * \return zero on success
+ * \return non-zero on failure
+ */
+int ast_event_queue_channel_state(struct ast_channel *chan);
+
 #if defined(__cplusplus) || defined(c_plusplus)
 }
 #endif

Modified: team/kmoore/channel-state-caching/include/asterisk/event_defs.h
URL: http://svnview.digium.com/svn/asterisk/team/kmoore/channel-state-caching/include/asterisk/event_defs.h?view=diff&rev=380840&r1=380839&r2=380840
==============================================================================
--- team/kmoore/channel-state-caching/include/asterisk/event_defs.h (original)
+++ team/kmoore/channel-state-caching/include/asterisk/event_defs.h Mon Feb  4 20:27:19 2013
@@ -60,8 +60,10 @@
 	AST_EVENT_ACL_CHANGE          = 0x0b,
 	/*! Send out a ping for debugging distributed events */
 	AST_EVENT_PING                = 0x0c,
+	/*! A channel state snapshot */
+	AST_EVENT_CHANNEL_STATE       = 0x0d,
 	/*! Number of event types.  This should be the last event type + 1 */
-	AST_EVENT_TOTAL               = 0x0d,
+	AST_EVENT_TOTAL               = 0x0e,
 };
 
 /*! \brief Event Information Element types */
@@ -304,8 +306,16 @@
 	 * Payload type: UINT
 	 */
 	AST_EVENT_IE_CACHABLE            = 0x003d,
+
+	/*!
+	 * \brief Channel state summary
+	 * Used by: AST_EVENT_CHANNEL_STATE
+	 * Payload type: RAW
+	 */
+	AST_EVENT_IE_CHANNEL_STATE       = 0x003e,
+
 	/*! \brief Must be the last IE value +1 */
-	AST_EVENT_IE_TOTAL               = 0x003e,
+	AST_EVENT_IE_TOTAL               = 0x003f,
 };
 
 /*!

Modified: team/kmoore/channel-state-caching/main/channel.c
URL: http://svnview.digium.com/svn/asterisk/team/kmoore/channel-state-caching/main/channel.c?view=diff&rev=380840&r1=380839&r2=380840
==============================================================================
--- team/kmoore/channel-state-caching/main/channel.c (original)
+++ team/kmoore/channel-state-caching/main/channel.c Mon Feb  4 20:27:19 2013
@@ -1145,34 +1145,7 @@
 	 * a lot of data into this func to do it here!
 	 */
 	if (ast_get_channel_tech(tech) || (tech2 && ast_get_channel_tech(tech2))) {
-		/*** DOCUMENTATION
-			<managerEventInstance>
-				<synopsis>Raised when a new channel is created.</synopsis>
-				<syntax>
-					<xi:include xpointer="xpointer(/docs/managerEvent[@name='Newstate']/managerEventInstance/syntax/parameter[@name='ChannelState'])" />
-					<xi:include xpointer="xpointer(/docs/managerEvent[@name='Newstate']/managerEventInstance/syntax/parameter[@name='ChannelStateDesc'])" />
-				</syntax>
-			</managerEventInstance>
-		***/
-		ast_manager_event(tmp, EVENT_FLAG_CALL, "Newchannel",
-			"Channel: %s\r\n"
-			"ChannelState: %d\r\n"
-			"ChannelStateDesc: %s\r\n"
-			"CallerIDNum: %s\r\n"
-			"CallerIDName: %s\r\n"
-			"AccountCode: %s\r\n"
-			"Exten: %s\r\n"
-			"Context: %s\r\n"
-			"Uniqueid: %s\r\n",
-			ast_channel_name(tmp),
-			state,
-			ast_state2str(state),
-			S_OR(cid_num, ""),
-			S_OR(cid_name, ""),
-			ast_channel_accountcode(tmp),
-			S_OR(exten, ""),
-			S_OR(context, ""),
-			ast_channel_uniqueid(tmp));
+		ast_event_queue_channel_state(tmp);
 	}
 
 	ast_channel_internal_finalize(tmp);
@@ -2893,39 +2866,8 @@
 	ast_channel_unlock(chan);
 
 	ast_cc_offer(chan);
-	/*** DOCUMENTATION
-		<managerEventInstance>
-			<synopsis>Raised when a channel is hung up.</synopsis>
-				<syntax>
-					<parameter name="Cause">
-						<para>A numeric cause code for why the channel was hung up.</para>
-					</parameter>
-					<parameter name="Cause-txt">
-						<para>A description of why the channel was hung up.</para>
-					</parameter>
-				</syntax>
-		</managerEventInstance>
-	***/
-	ast_manager_event(chan, EVENT_FLAG_CALL, "Hangup",
-		"Channel: %s\r\n"
-		"Uniqueid: %s\r\n"
-		"CallerIDNum: %s\r\n"
-		"CallerIDName: %s\r\n"
-		"ConnectedLineNum: %s\r\n"
-		"ConnectedLineName: %s\r\n"
-		"AccountCode: %s\r\n"
-		"Cause: %d\r\n"
-		"Cause-txt: %s\r\n",
-		ast_channel_name(chan),
-		ast_channel_uniqueid(chan),
-		S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, "<unknown>"),
-		S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, "<unknown>"),
-		S_COR(ast_channel_connected(chan)->id.number.valid, ast_channel_connected(chan)->id.number.str, "<unknown>"),
-		S_COR(ast_channel_connected(chan)->id.name.valid, ast_channel_connected(chan)->id.name.str, "<unknown>"),
-		ast_channel_accountcode(chan),
-		ast_channel_hangupcause(chan),
-		ast_cause2str(ast_channel_hangupcause(chan))
-		);
+
+	ast_event_queue_channel_state(chan);
 
 	if (ast_channel_cdr(chan) && !ast_test_flag(ast_channel_cdr(chan), AST_CDR_FLAG_BRIDGED) &&
 		!ast_test_flag(ast_channel_cdr(chan), AST_CDR_FLAG_POST_DISABLED) &&
@@ -7437,47 +7379,7 @@
 	 * we override what they are saying the state is and things go amuck. */
 	ast_devstate_changed_literal(AST_DEVICE_UNKNOWN, (ast_test_flag(ast_channel_flags(chan), AST_FLAG_DISABLE_DEVSTATE_CACHE) ? AST_DEVSTATE_NOT_CACHABLE : AST_DEVSTATE_CACHABLE), name);
 
-	/* setstate used to conditionally report Newchannel; this is no more */
-	/*** DOCUMENTATION
-		<managerEventInstance>
-			<synopsis>Raised when a channel's state changes.</synopsis>
-			<syntax>
-				<parameter name="ChannelState">
-					<para>A numeric code for the channel's current state, related to ChannelStateDesc</para>
-				</parameter>
-				<parameter name="ChannelStateDesc">
-					<enumlist>
-						<enum name="Down"/>
-						<enum name="Rsrvd"/>
-						<enum name="OffHook"/>
-						<enum name="Dialing"/>
-						<enum name="Ring"/>
-						<enum name="Ringing"/>
-						<enum name="Up"/>
-						<enum name="Busy"/>
-						<enum name="Dialing Offhook"/>
-						<enum name="Pre-ring"/>
-						<enum name="Unknown"/>
-					</enumlist>
-				</parameter>
-			</syntax>
-		</managerEventInstance>
-	***/
-	ast_manager_event(chan, EVENT_FLAG_CALL, "Newstate",
-		"Channel: %s\r\n"
-		"ChannelState: %d\r\n"
-		"ChannelStateDesc: %s\r\n"
-		"CallerIDNum: %s\r\n"
-		"CallerIDName: %s\r\n"
-		"ConnectedLineNum: %s\r\n"
-		"ConnectedLineName: %s\r\n"
-		"Uniqueid: %s\r\n",
-		ast_channel_name(chan), ast_channel_state(chan), ast_state2str(ast_channel_state(chan)),
-		S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, ""),
-		S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, ""),
-		S_COR(ast_channel_connected(chan)->id.number.valid, ast_channel_connected(chan)->id.number.str, ""),
-		S_COR(ast_channel_connected(chan)->id.name.valid, ast_channel_connected(chan)->id.name.str, ""),
-		ast_channel_uniqueid(chan));
+	ast_event_queue_channel_state(chan);
 
 	return 0;
 }
@@ -11281,3 +11183,55 @@
 {
 	ao2_unlink(channels, chan);
 }
+
+struct ast_channel_snapshot *ast_channel_snapshot_create(struct ast_channel *chan)
+{
+	struct ast_channel_snapshot *snapshot;
+
+	snapshot = ast_calloc(1, sizeof(*snapshot));
+	if (!snapshot) {
+		return NULL;
+	}
+
+	// fill out string fields here
+	ast_copy_string(snapshot->name, ast_channel_name(chan), sizeof(snapshot->name));
+	ast_copy_string(snapshot->accountcode, ast_channel_accountcode(chan), sizeof(snapshot->accountcode));
+	ast_copy_string(snapshot->peeraccount, ast_channel_peeraccount(chan), sizeof(snapshot->peeraccount));
+	ast_copy_string(snapshot->userfield, ast_channel_userfield(chan), sizeof(snapshot->userfield));
+	ast_copy_string(snapshot->uniqueid, ast_channel_uniqueid(chan), sizeof(snapshot->uniqueid));
+	ast_copy_string(snapshot->linkedid, ast_channel_linkedid(chan), sizeof(snapshot->linkedid));
+	ast_copy_string(snapshot->parkinglot, ast_channel_parkinglot(chan), sizeof(snapshot->parkinglot));
+	ast_copy_string(snapshot->hangupsource, ast_channel_hangupsource(chan), sizeof(snapshot->hangupsource));
+	if (ast_channel_appl(chan)) {
+		ast_copy_string(snapshot->appl, ast_channel_appl(chan), sizeof(snapshot->appl));
+	}
+	if (ast_channel_data(chan)) {
+		ast_copy_string(snapshot->data, ast_channel_data(chan), sizeof(snapshot->data));
+	}
+	ast_copy_string(snapshot->context, ast_channel_context(chan), sizeof(snapshot->context));
+	ast_copy_string(snapshot->exten, ast_channel_exten(chan), sizeof(snapshot->exten));
+
+	ast_copy_string(snapshot->caller_name,
+		S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, ""),
+		sizeof(snapshot->caller_name));
+	ast_copy_string(snapshot->caller_number,
+		S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, ""),
+		sizeof(snapshot->caller_number));
+
+	ast_copy_string(snapshot->connected_name,
+		S_COR(ast_channel_connected(chan)->id.name.valid, ast_channel_connected(chan)->id.name.str, ""),
+		sizeof(snapshot->connected_name));
+	ast_copy_string(snapshot->connected_number,
+		S_COR(ast_channel_connected(chan)->id.number.valid, ast_channel_connected(chan)->id.number.str, ""),
+		sizeof(snapshot->connected_number));
+
+	// copy other information
+	snapshot->creationtime = ast_channel_creationtime(chan);
+	snapshot->state = ast_channel_state(chan);
+	snapshot->priority = ast_channel_priority(chan);
+	snapshot->amaflags = ast_channel_amaflags(chan);
+	snapshot->hangupcause = ast_channel_hangupcause(chan);
+	snapshot->flags = *ast_channel_flags(chan);
+
+	return snapshot;
+}

Modified: team/kmoore/channel-state-caching/main/event.c
URL: http://svnview.digium.com/svn/asterisk/team/kmoore/channel-state-caching/main/event.c?view=diff&rev=380840&r1=380839&r2=380840
==============================================================================
--- team/kmoore/channel-state-caching/main/event.c (original)
+++ team/kmoore/channel-state-caching/main/event.c Mon Feb  4 20:27:19 2013
@@ -43,6 +43,7 @@
 #include "asterisk/taskprocessor.h"
 #include "asterisk/astobj2.h"
 #include "asterisk/cli.h"
+#include "asterisk/channel.h"
 
 static struct ast_taskprocessor *event_dispatcher;
 
@@ -143,6 +144,7 @@
 static int ast_event_hash_devstate(const void *obj, const int flags);
 static int ast_event_hash_devstate_change(const void *obj, const int flags);
 static int ast_event_hash_presence_state_change(const void *obj, const int flags);
+static int ast_event_hash_channel_state(const void *obj, const int flags);
 
 #ifdef LOW_MEMORY
 #define NUM_CACHE_BUCKETS 17
@@ -191,6 +193,10 @@
 		.hash_fn = ast_event_hash_presence_state_change,
 		.cache_args = { AST_EVENT_IE_PRESENCE_STATE, },
 	},
+	[AST_EVENT_CHANNEL_STATE] = {
+		.hash_fn = ast_event_hash_channel_state,
+		.cache_args = { AST_EVENT_IE_CEL_CHANNAME},
+	},
 
 };
 
@@ -215,6 +221,7 @@
 	[AST_EVENT_CEL]                 = "CEL",
 	[AST_EVENT_SECURITY]            = "Security",
 	[AST_EVENT_NETWORK_CHANGE]      = "NetworkChange",
+	[AST_EVENT_CHANNEL_STATE]       = "ChannelState",
 };
 
 /*!
@@ -279,7 +286,8 @@
 	[AST_EVENT_IE_RECEIVED_HASH]       = { AST_EVENT_IE_PLTYPE_STR,  "ReceivedHash" },
 	[AST_EVENT_IE_USING_PASSWORD]      = { AST_EVENT_IE_PLTYPE_UINT, "UsingPassword" },
 	[AST_EVENT_IE_ATTEMPTED_TRANSPORT] = { AST_EVENT_IE_PLTYPE_STR,  "AttemptedTransport" },
-	[AST_EVENT_IE_CACHABLE]            = { AST_EVENT_IE_PLTYPE_UINT,  "Cachable" },
+	[AST_EVENT_IE_CACHABLE]            = { AST_EVENT_IE_PLTYPE_UINT, "Cachable" },
+	[AST_EVENT_IE_CHANNEL_STATE]       = { AST_EVENT_IE_PLTYPE_RAW,  "ChannelState" },
 };
 
 const char *ast_event_get_type_name(const struct ast_event *event)
@@ -1638,6 +1646,22 @@
 
 /*!
  * \internal
+ * \brief Hash function for AST_EVENT_CHANNEL_STATE
+ *
+ * \param[in] obj an ast_event
+ * \param[in] flags unused
+ *
+ * \return hash value
+ */
+static int ast_event_hash_channel_state(const void *obj, const int flags)
+{
+	const struct ast_event *event = obj;
+
+	return ast_str_hash(ast_event_get_ie_str(event, AST_EVENT_IE_CEL_CHANNAME));
+}
+
+/*!
+ * \internal
  * \brief Compare two events
  *
  * \param[in] obj the first event, as an ast_event_ref
@@ -1884,3 +1908,28 @@
 {
 	return sizeof(struct ast_event);
 }
+
+int ast_event_queue_channel_state(struct ast_channel *chan)
+{
+	struct ast_channel_snapshot *snapshot = ast_channel_snapshot_create(chan);
+	struct ast_event *newchannel_event;
+	if (!snapshot) {
+		return -1;
+	}
+	newchannel_event = ast_event_new(AST_EVENT_CHANNEL_STATE,
+		AST_EVENT_IE_CEL_CHANNAME,          AST_EVENT_IE_PLTYPE_STR, ast_channel_name(chan),
+		AST_EVENT_IE_CHANNEL_STATE,         AST_EVENT_IE_PLTYPE_RAW, snapshot, sizeof(*snapshot),
+		AST_EVENT_IE_END);
+
+	ast_free(snapshot);
+	if (!newchannel_event) {
+		return -1;
+	}
+
+	if (ast_test_flag(ast_channel_flags(chan), AST_FLAG_DISABLE_DEVSTATE_CACHE)) {
+		ast_event_queue(newchannel_event);
+	} else {
+		ast_event_queue_and_cache(newchannel_event);
+	}
+	return 0;
+}

Modified: team/kmoore/channel-state-caching/main/manager.c
URL: http://svnview.digium.com/svn/asterisk/team/kmoore/channel-state-caching/main/manager.c?view=diff&rev=380840&r1=380839&r2=380840
==============================================================================
--- team/kmoore/channel-state-caching/main/manager.c (original)
+++ team/kmoore/channel-state-caching/main/manager.c Mon Feb  4 20:27:19 2013
@@ -1031,6 +1031,8 @@
 static int authtimeout;
 static int authlimit;
 static char *manager_channelvars;
+static struct ast_event_sub *channel_state_sub; /*!< Subscription for channel state changes */
+struct ao2_container *channel_state_cache;	/*!< Holds channel state for diffs */
 
 #define DEFAULT_REALM		"asterisk"
 static char global_realm[MAXHOSTNAMELEN];	/*!< Default realm */
@@ -7343,6 +7345,180 @@
 	AST_RWLIST_UNLOCK(&channelvars);
 }
 
+static void manager_newchannel(const struct ast_channel_snapshot *snapshot)
+{
+	/*** DOCUMENTATION
+		<managerEventInstance>
+			<synopsis>Raised when a new channel is created.</synopsis>
+			<syntax>
+				<xi:include xpointer="xpointer(/docs/managerEvent[@name='Newstate']/managerEventInstance/syntax/parameter[@name='ChannelState'])" />
+				<xi:include xpointer="xpointer(/docs/managerEvent[@name='Newstate']/managerEventInstance/syntax/parameter[@name='ChannelStateDesc'])" />
+			</syntax>
+		</managerEventInstance>
+	***/
+	manager_event(EVENT_FLAG_CALL, "Newchannel",
+		"Channel: %s\r\n"
+		"ChannelState: %d\r\n"
+		"ChannelStateDesc: %s\r\n"
+		"CallerIDNum: %s\r\n"
+		"CallerIDName: %s\r\n"
+		"AccountCode: %s\r\n"
+		"Exten: %s\r\n"
+		"Context: %s\r\n"
+		"Uniqueid: %s\r\n",
+		snapshot->name,
+		snapshot->state,
+		ast_state2str(snapshot->state),
+		snapshot->caller_number,
+		snapshot->caller_name,
+		snapshot->accountcode,
+		snapshot->exten,
+		snapshot->context,
+		snapshot->uniqueid);
+}
+
+static void manager_newstate(const struct ast_channel_snapshot *snapshot)
+{
+	/*** DOCUMENTATION
+		<managerEventInstance>
+			<synopsis>Raised when a channel's state changes.</synopsis>
+			<syntax>
+				<parameter name="ChannelState">
+					<para>A numeric code for the channel's current state, related to ChannelStateDesc</para>
+				</parameter>
+				<parameter name="ChannelStateDesc">
+					<enumlist>
+						<enum name="Down"/>
+						<enum name="Rsrvd"/>
+						<enum name="OffHook"/>
+						<enum name="Dialing"/>
+						<enum name="Ring"/>
+						<enum name="Ringing"/>
+						<enum name="Up"/>
+						<enum name="Busy"/>
+						<enum name="Dialing Offhook"/>
+						<enum name="Pre-ring"/>
+						<enum name="Unknown"/>
+					</enumlist>
+				</parameter>
+			</syntax>
+		</managerEventInstance>
+	***/
+	manager_event(EVENT_FLAG_CALL, "Newstate",
+		"Channel: %s\r\n"
+		"ChannelState: %d\r\n"
+		"ChannelStateDesc: %s\r\n"
+		"CallerIDNum: %s\r\n"
+		"CallerIDName: %s\r\n"
+		"ConnectedLineNum: %s\r\n"
+		"ConnectedLineName: %s\r\n"
+		"Uniqueid: %s\r\n",
+		snapshot->name,
+		snapshot->state,
+		ast_state2str(snapshot->state),
+		snapshot->caller_number,
+		snapshot->caller_name,
+		snapshot->connected_number,
+		snapshot->connected_name,
+		snapshot->uniqueid);
+}
+
+static void manager_hangup(const struct ast_channel_snapshot *snapshot)
+{
+	/*** DOCUMENTATION
+		<managerEventInstance>
+			<synopsis>Raised when a channel is hung up.</synopsis>
+				<syntax>
+					<parameter name="Cause">
+						<para>A numeric cause code for why the channel was hung up.</para>
+					</parameter>
+					<parameter name="Cause-txt">
+						<para>A description of why the channel was hung up.</para>
+					</parameter>
+				</syntax>
+		</managerEventInstance>
+	***/
+	manager_event(EVENT_FLAG_CALL, "Hangup",
+		"Channel: %s\r\n"
+		"Uniqueid: %s\r\n"
+		"CallerIDNum: %s\r\n"
+		"CallerIDName: %s\r\n"
+		"ConnectedLineNum: %s\r\n"
+		"ConnectedLineName: %s\r\n"
+		"AccountCode: %s\r\n"
+		"Cause: %d\r\n"
+		"Cause-txt: %s\r\n",
+		snapshot->name,
+		snapshot->uniqueid,
+		snapshot->caller_number,
+		snapshot->caller_name,
+		snapshot->connected_number,
+		snapshot->connected_name,
+		snapshot->accountcode,
+		snapshot->hangupcause,
+		ast_cause2str(snapshot->hangupcause));
+}
+
+static int channel_state_hash_fn(const void *obj, const int flags)
+{
+        const struct ast_channel_snapshot *data = obj;
+        return ast_str_case_hash(data->name);
+}
+
+static int channel_state_cmp_fn(void *obj, void *arg, int flags)
+{
+        const struct ast_channel_snapshot *one = obj, *two = arg;
+        return !strcasecmp(one->name, two->name) ? CMP_MATCH | CMP_STOP : 0;
+}
+
+static void channel_state_cb(const struct ast_event *event, void *userdata)
+{
+	const struct ast_channel_snapshot *new_snapshot = ast_event_get_ie_raw(event, AST_EVENT_IE_CHANNEL_STATE);
+	struct ast_channel_snapshot *old_snapshot = NULL, *ao2_snapshot = NULL;
+	int is_hungup = ast_test_flag(&new_snapshot->flags, AST_FLAG_ZOMBIE) ? 1 : 0;
+
+	/* Only allocate for a new AO2 object if the channel is not hanging up */
+	if (!is_hungup) {
+		ao2_snapshot = ao2_alloc(sizeof(*new_snapshot), NULL);
+	}
+
+	/* Only remove the old cached state if we can insert a new one or the channel is being hung up */
+	ao2_lock(channel_state_cache);
+	if (ao2_snapshot || is_hungup) {
+		old_snapshot = ao2_find(channel_state_cache, new_snapshot->name, OBJ_KEY | OBJ_UNLINK);
+	} else {
+		/* Not hanging up and allocation failed.  Leave the old snapshot in place.
+		 * This may cause duplicate events, but it's better than missing an event. */
+		old_snapshot = ao2_find(channel_state_cache, new_snapshot->name, OBJ_KEY);
+	}
+
+	/* Link in the new snapshot if available */
+	if (ao2_snapshot) {
+		memcpy(ao2_snapshot, new_snapshot, sizeof(*ao2_snapshot));
+		ao2_link(channel_state_cache, ao2_snapshot);
+		ao2_t_ref(ao2_snapshot, -1, "decrement ref for ao2_snapshot from creation since it is now linked and no longer needed");
+		ao2_snapshot = NULL;
+	}
+	ao2_unlock(channel_state_cache);
+
+	if (!old_snapshot) {
+		manager_newchannel(new_snapshot);
+	}
+
+	if (old_snapshot && new_snapshot->state != old_snapshot->state) {
+		manager_newstate(new_snapshot);
+	}
+
+	if (is_hungup) {
+		manager_hangup(new_snapshot);
+	}
+
+	if (old_snapshot) {
+		ao2_t_ref(old_snapshot, -1, "decrement ref for old_snapshot from ao2_find");
+		old_snapshot = NULL;
+	}
+}
+
 /*! \internal \brief Free a user record.  Should already be removed from the list */
 static void manager_free_user(struct ast_manager_user *user)
 {
@@ -7365,6 +7541,16 @@
 static void manager_shutdown(void)
 {
 	struct ast_manager_user *user;
+
+	if (channel_state_sub) {
+		ast_event_unsubscribe(channel_state_sub);
+		channel_state_sub = NULL;
+	}
+
+	if (channel_state_cache) {
+		ao2_t_callback(channel_state_cache, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL, "unlink all channel state cache items");
+		ao2_t_ref(channel_state_cache, -1 , "decrement ref for channel state cache");
+	}
 
 	if (registered) {
 		ast_manager_unregister("Ping");
@@ -7436,6 +7622,8 @@
 	}
 }
 
+#define CHANNEL_STATE_CACHE_BUCKETS 37
+
 static int __init_manager(int reload, int by_external_config)
 {
 	struct ast_config *ucfg = NULL, *cfg = NULL;
@@ -7456,6 +7644,24 @@
 	int acl_subscription_flag = 0;
 
 	manager_enabled = 0;
+
+	if (!channel_state_cache) {
+		channel_state_cache = ao2_container_alloc(CHANNEL_STATE_CACHE_BUCKETS, channel_state_hash_fn, channel_state_cmp_fn);
+		if (!channel_state_cache) {
+			ast_log(LOG_NOTICE, "Unable to create channel state cache.\n");
+			return -1;
+		}
+	}
+
+	if (!channel_state_sub) {
+		channel_state_sub = ast_event_subscribe(AST_EVENT_CHANNEL_STATE,
+			channel_state_cb, "Manager Channel State Subscription",
+			NULL, AST_EVENT_IE_END);
+		if (!channel_state_sub) {
+			ast_log(LOG_NOTICE, "Unable to create channel state subscription.\n");
+			return -1;
+		}
+	}
 
 	if (!registered) {
 		/* Register default actions */




More information about the asterisk-commits mailing list