[asterisk-commits] rmudgett: branch rmudgett/bridge_phase r391760 - /team/rmudgett/bridge_phase/...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Thu Jun 13 20:57:44 CDT 2013


Author: rmudgett
Date: Thu Jun 13 20:57:42 2013
New Revision: 391760

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=391760
Log:
Add agent_pvt and support code with preliminary device state code.

Modified:
    team/rmudgett/bridge_phase/apps/app_agent_pool.c

Modified: team/rmudgett/bridge_phase/apps/app_agent_pool.c
URL: http://svnview.digium.com/svn/asterisk/team/rmudgett/bridge_phase/apps/app_agent_pool.c?view=diff&rev=391760&r1=391759&r2=391760
==============================================================================
--- team/rmudgett/bridge_phase/apps/app_agent_pool.c (original)
+++ team/rmudgett/bridge_phase/apps/app_agent_pool.c Thu Jun 13 20:57:42 2013
@@ -376,8 +376,11 @@
 	return cfg;
 }
 
+static void agents_post_apply_config(void);
+
 CONFIG_INFO_STANDARD(cfg_info, cfg_handle, agents_cfg_alloc,
 	.files = ACO_FILES(&agents_conf),
+	.post_apply_config = agents_post_apply_config,
 );
 
 /*!
@@ -500,19 +503,295 @@
 	return -1;
 }
 
+/*! Agent config option override flags. */
+enum agent_override_flags {
+	AGENT_FLAG_ACK_CALL = (1 << 0),
+	AGENT_FLAG_END_CALL = (1 << 1),
+	AGENT_FLAG_DTMF_ACCEPT = (1 << 2),
+	AGENT_FLAG_DTMF_END = (1 << 3),
+	AGENT_FLAG_AUTO_LOGOFF = (1 << 4),
+	AGENT_FLAG_WRAPUP_TIME = (1 << 5),
+	AGENT_FLAG_MAX_LOGIN_TRIES = (1 << 6),
+};
+
+/*! \brief Structure representing an agent. */
+struct agent_pvt {
+	AST_DECLARE_STRING_FIELDS(
+		/*! Identification of the agent.  (agents container key) */
+		AST_STRING_FIELD(username);
+		/*! Login override DTMF string for an agent to accept a call. */
+		AST_STRING_FIELD(override_dtmf_accept);
+		/*! Login override DTMF string for an agent to end a call. */
+		AST_STRING_FIELD(override_dtmf_end);
+	);
+	/*! Flags show if settings were overridden by channel vars. */
+	unsigned int flags;
+	/*! Login override number of failed login attempts allowed. */
+	unsigned int override_max_login_tries;
+	/*! Login override number of seconds for agent to ack a call before being logged off. */
+	unsigned int override_auto_logoff;
+	/*! Login override time after a call in ms before the agent can get a new call. */
+	unsigned int override_wrapup_time;
+	/*! Login override if agent needs to ack a call to accept it. */
+	int override_ack_call:1;
+	/*! Login override if agent can use DTMF to end a call. */
+	int override_end_call:1;
+
+	/*! Mark and sweep config update to determine if an agent is dead. */
+	unsigned int the_mark:1;
+	/*!
+	 * \brief TRUE if the agent is waiting to die.
+	 *
+	 * \note Agents cannot log in if they are dead.
+	 *
+	 * \note Agents destroy themselves when they are in the agent
+	 * holding bridge.
+	 */
+	unsigned int dead:1;
+	/*! TRUE if we joined the logged in channel to the bridging system. */
+	unsigned int we_joined:1;
+
+	/*! Custom device state of agent. */
+	enum ast_device_state state;
+
+	/*! When agent first logged in */
+	time_t start_login;
+	/*! When call started */
+	time_t start_call;
+	/*! When last disconnected */
+	struct timeval last_disconnect;
+
+	/*! Agent is logged in with this channel. (Holds ref) (NULL if not logged in.) */
+	struct ast_channel *chan;
+	/*! Active config values from config file. (Holds ref) */
+	struct agent_cfg *cfg;
+};
+
+static void agent_pvt_destructor(void *vdoomed)
+{
+	struct agent_pvt *doomed = vdoomed;
+
+	if (doomed->chan) {
+		doomed->chan = ast_channel_unref(doomed->chan);
+	}
+	ao2_cleanup(doomed->cfg);
+	doomed->cfg = NULL;
+	ast_string_field_free_memory(doomed);
+}
+
+static struct agent_pvt *agent_pvt_new(struct agent_cfg *cfg)
+{
+	struct agent_pvt *agent;
+
+	agent = ao2_alloc(sizeof(*agent), agent_pvt_destructor);
+	if (!agent || ast_string_field_init(agent, 32)) {
+		return NULL;
+	}
+	ast_string_field_set(agent, username, cfg->username);
+	ao2_ref(cfg, +1);
+	agent->cfg = cfg;
+	agent->state = AST_DEVICE_UNAVAILABLE;
+	return agent;
+}
+
+/*! Container of defined agents. */
+static struct ao2_container *agents;
+
+/*!
+ * \internal
+ * \brief Agents ao2 container sort function.
+ * \since 12.0.0
+ *
+ * \param obj_left pointer to the (user-defined part) of an object.
+ * \param obj_right pointer to the (user-defined part) of an object.
+ * \param flags flags from ao2_callback()
+ *   OBJ_POINTER - if set, 'obj_right', is an object.
+ *   OBJ_KEY - if set, 'obj_right', is a search key item that is not an object.
+ *   OBJ_PARTIAL_KEY - if set, 'obj_right', is a partial search key item that is not an object.
+ *
+ * \retval <0 if obj_left < obj_right
+ * \retval =0 if obj_left == obj_right
+ * \retval >0 if obj_left > obj_right
+ */
+static int agent_pvt_sort_cmp(const void *obj_left, const void *obj_right, int flags)
+{
+	const struct agent_pvt *agent_left = obj_left;
+	const struct agent_pvt *agent_right = obj_right;
+	const char *right_key = obj_right;
+	int cmp;
+
+	switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
+	default:
+	case OBJ_POINTER:
+		right_key = agent_right->username;
+		/* Fall through */
+	case OBJ_KEY:
+		cmp = strcmp(agent_left->username, right_key);
+		break;
+	case OBJ_PARTIAL_KEY:
+		cmp = strncmp(agent_left->username, right_key, strlen(right_key));
+		break;
+	}
+	return cmp;
+}
+
+/*!
+ * \internal
+ * \brief ao2_find() callback function.
+ * \since 12.0.0
+ *
+ * Usage:
+ * found = ao2_find(agents, agent, OBJ_POINTER);
+ * found = ao2_find(agents, "agent-id", OBJ_KEY);
+ * found = ao2_find(agents, agent->chan, 0);
+ */
+static int agent_pvt_cmp(void *obj, void *arg, int flags)
+{
+	const struct agent_pvt *agent = obj;
+	int cmp;
+
+	switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
+	case OBJ_POINTER:
+	case OBJ_KEY:
+	case OBJ_PARTIAL_KEY:
+		cmp = CMP_MATCH;
+		break;
+	default:
+		if (agent->chan == arg) {
+			cmp = CMP_MATCH;
+		} else {
+			cmp = 0;
+		}
+		break;
+	}
+	return cmp;
+}
+
+/*!
+ * \internal
+ * \brief Get the agent device state.
+ * \since 12.0.0
+ *
+ * \param agent_id Username of the agent.
+ *
+ * \details
+ * Search the agents container for the agent and return the
+ * current state.
+ *
+ * \return Device state of the agent.
+ */
+static enum ast_device_state agent_pvt_devstate_get(const char *agent_id)
+{
+	RAII_VAR(struct agent_pvt *, agent, ao2_find(agents, agent_id, OBJ_KEY), ao2_cleanup);
+
+	if (agent) {
+		return agent->state;
+	}
+	return AST_DEVICE_INVALID;
+}
+
+static int agent_mark(void *obj, void *arg, int flags)
+{
+	struct agent_pvt *agent = obj;
+
+	ao2_lock(agent);
+	agent->the_mark = 1;
+	ao2_unlock(agent);
+	return 0;
+}
+
+static void agents_mark(void)
+{
+	ao2_callback(agents, 0, agent_mark, NULL);
+}
+
+static int agent_sweep(void *obj, void *arg, int flags)
+{
+	struct agent_pvt *agent = obj;
+	int cmp = 0;
+
+	ao2_lock(agent);
+	if (agent->the_mark) {
+		agent->the_mark = 0;
+		agent->dead = 1;
+		if (!agent->chan) {
+			/* Agent isn't logged in at this time.  Destroy it now. */
+			cmp = CMP_MATCH;
+		}
+	} else {
+		/* Resurect a dead agent if it hasn't left yet or is still on a call. */
+		agent->dead = 0;
+	}
+	ao2_unlock(agent);
+	return cmp;
+}
+
+static void agents_sweep(void)
+{
+	ao2_callback(agents, OBJ_MULTIPLE | OBJ_UNLINK | OBJ_NODATA, agent_sweep, NULL);
+}
+
+static void agents_post_apply_config(void)
+{
+	struct ao2_iterator iter;
+	struct agent_cfg *cfg;
+	RAII_VAR(struct agents_cfg *, cfgs, ao2_global_obj_ref(cfg_handle), ao2_cleanup);
+
+	ast_assert(cfgs != NULL);
+
+	agents_mark();
+	iter = ao2_iterator_init(cfgs->agents, 0);
+	for (; (cfg = ao2_iterator_next(&iter)); ao2_ref(cfg, -1)) {
+		RAII_VAR(struct agent_pvt *, agent, ao2_find(agents, cfg->username, OBJ_KEY), ao2_cleanup);
+
+		if (agent) {
+			ao2_lock(agent);
+			agent->the_mark = 0;
+			ao2_unlock(agent);
+			continue;
+		}
+		agent = agent_pvt_new(cfg);
+		if (!agent) {
+			continue;
+		}
+		ao2_link(agents, agent);
+	}
+	ao2_iterator_destroy(&iter);
+	agents_sweep();
+}
+
 static int unload_module(void)
 {
+	ast_devstate_prov_del("Agent");
 	destroy_config();
+	ao2_ref(agents, -1);
+	agents = NULL;
 	return 0;
 }
 
 static int load_module(void)
 {
+	int res = 0;
+
+	agents = ao2_container_alloc_rbtree(AO2_ALLOC_OPT_LOCK_MUTEX,
+		AO2_CONTAINER_ALLOC_OPT_DUPS_REPLACE, agent_pvt_sort_cmp, agent_pvt_cmp);
+	if (!agents) {
+		return AST_MODULE_LOAD_FAILURE;
+	}
 	if (load_config()) {
 		ast_log(LOG_ERROR, "Unable to load config. Not loading module.\n");
+		ao2_ref(agents, -1);
+		agents = NULL;
 		return AST_MODULE_LOAD_DECLINE;
 	}
 
+	/* Setup to provide Agent:agent-id device state. */
+	res |= ast_devstate_prov_add("Agent", agent_pvt_devstate_get);
+
+	if (res) {
+		unload_module();
+		return AST_MODULE_LOAD_FAILURE;
+	}
 	return AST_MODULE_LOAD_SUCCESS;
 }
 




More information about the asterisk-commits mailing list