2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 2013 Digium, Inc.
6 * Richard Mudgett <rmudgett@digium.com>
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
21 * \brief Call center agent pool.
23 * \author Richard Mudgett <rmudgett@digium.com>
26 * \arg \ref AstCREDITS
27 * \arg \ref Config_agent
30 <support_level>core</support_level>
36 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
38 #include "asterisk/cli.h"
39 #include "asterisk/app.h"
40 #include "asterisk/pbx.h"
41 #include "asterisk/module.h"
42 #include "asterisk/channel.h"
43 #include "asterisk/bridge.h"
44 #include "asterisk/bridge_internal.h"
45 #include "asterisk/bridge_basic.h"
46 #include "asterisk/bridge_after.h"
47 #include "asterisk/config_options.h"
48 #include "asterisk/features_config.h"
49 #include "asterisk/astobj2.h"
50 #include "asterisk/stringfields.h"
51 #include "asterisk/stasis_channels.h"
52 #include "asterisk/causes.h"
55 <application name="AgentLogin" language="en_US">
60 <parameter name="AgentId" required="true" />
61 <parameter name="options">
64 <para>silent login - do not announce the login ok segment after
65 agent logged on.</para>
71 <para>Login an agent to the system. Any agent authentication is assumed to
72 already be done by dialplan. While logged in, the agent can receive calls
73 and will hear the sound file specified by the config option custom_beep
74 when a new call comes in for the agent. Login failures will continue in
75 the dialplan with <variable>AGENT_STATUS</variable> set.</para>
76 <para>Before logging in, you can setup on the real agent channel the
77 CHANNEL(dtmf-features) an agent will have when talking to a caller
78 and you can setup on the channel running this application the
79 CONNECTEDLINE() information the agent will see while waiting for a
81 <para><variable>AGENT_STATUS</variable> enumeration values:</para>
83 <enum name = "INVALID"><para>The specified agent is invalid.</para></enum>
84 <enum name = "ALREADY_LOGGED_IN"><para>The agent is already logged in.</para></enum>
86 <note><para>The Agents:<replaceable>AgentId</replaceable> device state is
87 available to monitor the status of the agent.</para></note>
90 <ref type="application">Authenticate</ref>
91 <ref type="application">Queue</ref>
92 <ref type="application">AddQueueMember</ref>
93 <ref type="application">RemoveQueueMember</ref>
94 <ref type="application">PauseQueueMember</ref>
95 <ref type="application">UnpauseQueueMember</ref>
96 <ref type="function">AGENT</ref>
97 <ref type="function">CHANNEL(dtmf-features)</ref>
98 <ref type="function">CONNECTEDLINE()</ref>
99 <ref type="filename">agents.conf</ref>
100 <ref type="filename">queues.conf</ref>
103 <application name="AgentRequest" language="en_US">
105 Request an agent to connect with the channel.
108 <parameter name="AgentId" required="true" />
111 <para>Request an agent to connect with the channel. Failure to find and
112 alert an agent will continue in the dialplan with <variable>AGENT_STATUS</variable> set.</para>
113 <para><variable>AGENT_STATUS</variable> enumeration values:</para>
115 <enum name = "INVALID"><para>The specified agent is invalid.</para></enum>
116 <enum name = "NOT_LOGGED_IN"><para>The agent is not available.</para></enum>
117 <enum name = "BUSY"><para>The agent is on another call.</para></enum>
118 <enum name = "ERROR"><para>Alerting the agent failed.</para></enum>
122 <ref type="application">AgentLogin</ref>
125 <function name="AGENT" language="en_US">
127 Gets information about an Agent
130 <parameter name="AgentId" required="true" />
131 <parameter name="item">
132 <para>The valid items to retrieve are:</para>
135 <para>(default) The status of the agent (LOGGEDIN | LOGGEDOUT)</para>
137 <enum name="password">
138 <para>Deprecated. The dialplan handles any agent authentication.</para>
141 <para>The name of the agent</para>
143 <enum name="mohclass">
144 <para>MusicOnHold class</para>
146 <enum name="channel">
147 <para>The name of the active channel for the Agent (AgentLogin)</para>
149 <enum name="fullchannel">
150 <para>The untruncated name of the active channel for the Agent (AgentLogin)</para>
155 <description></description>
157 <manager name="Agents" language="en_US">
159 Lists agents and their status.
162 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
165 <para>Will list info about all defined agents.</para>
168 <ref type="managerEvent">Agents</ref>
169 <ref type="managerEvent">AgentsComplete</ref>
172 <managerEvent language="en_US" name="Agents">
173 <managerEventInstance class="EVENT_FLAG_AGENT">
175 Response event in a series to the Agents AMI action containing
176 information about a defined agent.
179 <parameter name="Agent">
180 <para>Agent ID of the agent.</para>
182 <parameter name="Name">
183 <para>User friendly name of the agent.</para>
185 <parameter name="Status">
186 <para>Current status of the agent.</para>
187 <para>The valid values are:</para>
189 <enum name="AGENT_LOGGEDOFF" />
190 <enum name="AGENT_IDLE" />
191 <enum name="AGENT_ONCALL" />
194 <parameter name="TalkingToChan">
195 <para><variable>BRIDGEPEER</variable> value on agent channel.</para>
196 <para>Present if Status value is <literal>AGENT_ONCALL</literal>.</para>
198 <parameter name="CallStarted">
199 <para>Epoche time when the agent started talking with the caller.</para>
200 <para>Present if Status value is <literal>AGENT_ONCALL</literal>.</para>
202 <parameter name="LoggedInTime">
203 <para>Epoche time when the agent logged in.</para>
204 <para>Present if Status value is <literal>AGENT_IDLE</literal> or <literal>AGENT_ONCALL</literal>.</para>
207 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
210 <para>The channel snapshot is present if the Status value is <literal>AGENT_IDLE</literal> or <literal>AGENT_ONCALL</literal>.</para>
213 <ref type="manager">Agents</ref>
215 </managerEventInstance>
217 <managerEvent language="en_US" name="AgentsComplete">
218 <managerEventInstance class="EVENT_FLAG_AGENT">
220 Final response event in a series of events to the Agents AMI action.
223 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
226 <ref type="manager">Agents</ref>
228 </managerEventInstance>
230 <manager name="AgentLogoff" language="en_US">
232 Sets an agent as no longer logged in.
235 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
236 <parameter name="Agent" required="true">
237 <para>Agent ID of the agent to log off.</para>
239 <parameter name="Soft">
240 <para>Set to <literal>true</literal> to not hangup existing calls.</para>
244 <para>Sets an agent as no longer logged in.</para>
247 <configInfo name="app_agent_pool" language="en_US">
248 <synopsis>Agent pool applications</synopsis>
250 <note><para>Option changes take effect on agent login or after an agent
251 disconnects from a call.</para></note>
253 <configFile name="agents.conf">
254 <configObject name="global">
255 <synopsis>Unused, but reserved.</synopsis>
257 <configObject name="agent-id">
258 <synopsis>Configure an agent for the pool.</synopsis>
260 <xi:include xpointer="xpointer(/docs/configInfo[@name='app_agent_pool']/description/note)" />
262 <configOption name="ackcall">
263 <synopsis>Enable to require the agent to acknowledge a call.</synopsis>
265 <para>Enable to require the agent to give a DTMF acknowledgement
266 when the agent receives a call.</para>
267 <note><para>The option is overridden by <variable>AGENTACKCALL</variable> on agent login.</para></note>
268 <xi:include xpointer="xpointer(/docs/configInfo[@name='app_agent_pool']/description/note)" />
271 <configOption name="acceptdtmf">
272 <synopsis>DTMF key sequence the agent uses to acknowledge a call.</synopsis>
274 <note><para>The option is overridden by <variable>AGENTACCEPTDTMF</variable> on agent login.</para></note>
275 <note><para>The option is ignored unless the ackcall option is enabled.</para></note>
276 <xi:include xpointer="xpointer(/docs/configInfo[@name='app_agent_pool']/description/note)" />
279 <configOption name="autologoff">
280 <synopsis>Time the agent has to acknowledge a call before being logged off.</synopsis>
282 <para>Set how many seconds a call for the agent has to wait for the
283 agent to acknowledge the call before the agent is automatically
284 logged off. If set to zero then the call will wait forever for
285 the agent to acknowledge.</para>
286 <note><para>The option is overridden by <variable>AGENTAUTOLOGOFF</variable> on agent login.</para></note>
287 <note><para>The option is ignored unless the ackcall option is enabled.</para></note>
288 <xi:include xpointer="xpointer(/docs/configInfo[@name='app_agent_pool']/description/note)" />
291 <configOption name="wrapuptime">
292 <synopsis>Minimum time the agent has between calls.</synopsis>
294 <para>Set the minimum amount of time in milliseconds after
295 disconnecting a call before the agent can receive a new call.</para>
296 <note><para>The option is overridden by <variable>AGENTWRAPUPTIME</variable> on agent login.</para></note>
297 <xi:include xpointer="xpointer(/docs/configInfo[@name='app_agent_pool']/description/note)" />
300 <configOption name="musiconhold">
301 <synopsis>Music on hold class the agent listens to between calls.</synopsis>
303 <xi:include xpointer="xpointer(/docs/configInfo[@name='app_agent_pool']/description/note)" />
306 <configOption name="recordagentcalls">
307 <synopsis>Enable to automatically record calls the agent takes.</synopsis>
309 <para>Enable recording calls the agent takes automatically by
310 invoking the automixmon DTMF feature when the agent connects
311 to a caller. See <filename>features.conf.sample</filename> for information about
312 the automixmon feature.</para>
313 <xi:include xpointer="xpointer(/docs/configInfo[@name='app_agent_pool']/description/note)" />
316 <configOption name="custom_beep">
317 <synopsis>Sound file played to alert the agent when a call is present.</synopsis>
319 <xi:include xpointer="xpointer(/docs/configInfo[@name='app_agent_pool']/description/note)" />
322 <configOption name="fullname">
323 <synopsis>A friendly name for the agent used in log messages.</synopsis>
325 <xi:include xpointer="xpointer(/docs/configInfo[@name='app_agent_pool']/description/note)" />
333 /* ------------------------------------------------------------------- */
335 #define AST_MAX_BUF 256
337 /*! Maximum wait time (in ms) for the custom_beep file to play announcing the caller. */
338 #define CALLER_SAFETY_TIMEOUT_TIME (2 * 60 * 1000)
340 /*! Number of seconds to wait for local channel optimizations to complete. */
341 #define LOGIN_WAIT_TIMEOUT_TIME 5
343 static const char app_agent_login[] = "AgentLogin";
344 static const char app_agent_request[] = "AgentRequest";
346 /*! Agent config parameters. */
348 AST_DECLARE_STRING_FIELDS(
349 /*! Identification of the agent. (agents config container key) */
350 AST_STRING_FIELD(username);
351 /*! Name of agent for logging and querying purposes */
352 AST_STRING_FIELD(full_name);
355 * \brief DTMF string for an agent to accept a call.
357 * \note The channel variable AGENTACCEPTDTMF overrides on login.
359 AST_STRING_FIELD(dtmf_accept);
360 /*! Beep sound file to use. Alert the agent a call is waiting. */
361 AST_STRING_FIELD(beep_sound);
362 /*! MOH class to use while agent waiting for call. */
363 AST_STRING_FIELD(moh);
366 * \brief Number of seconds for agent to ack a call before being logged off.
368 * \note The channel variable AGENTAUTOLOGOFF overrides on login.
369 * \note If zero then timer is disabled.
371 unsigned int auto_logoff;
373 * \brief Time after a call in ms before the agent can get a new call.
375 * \note The channel variable AGENTWRAPUPTIME overrides on login.
377 unsigned int wrapup_time;
379 * \brief TRUE if agent needs to ack a call to accept it.
381 * \note The channel variable AGENTACKCALL overrides on login.
384 /*! TRUE if agent calls are automatically recorded. */
385 int record_agent_calls;
390 * \brief Agent config ao2 container sort function.
393 * \param obj_left pointer to the (user-defined part) of an object.
394 * \param obj_right pointer to the (user-defined part) of an object.
395 * \param flags flags from ao2_callback()
396 * OBJ_POINTER - if set, 'obj_right', is an object.
397 * OBJ_KEY - if set, 'obj_right', is a search key item that is not an object.
398 * OBJ_PARTIAL_KEY - if set, 'obj_right', is a partial search key item that is not an object.
400 * \retval <0 if obj_left < obj_right
401 * \retval =0 if obj_left == obj_right
402 * \retval >0 if obj_left > obj_right
404 static int agent_cfg_sort_cmp(const void *obj_left, const void *obj_right, int flags)
406 const struct agent_cfg *cfg_left = obj_left;
407 const struct agent_cfg *cfg_right = obj_right;
408 const char *right_key = obj_right;
411 switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
414 right_key = cfg_right->username;
417 cmp = strcmp(cfg_left->username, right_key);
419 case OBJ_PARTIAL_KEY:
420 cmp = strncmp(cfg_left->username, right_key, strlen(right_key));
426 static void agent_cfg_destructor(void *vdoomed)
428 struct agent_cfg *doomed = vdoomed;
430 ast_string_field_free_memory(doomed);
433 static void *agent_cfg_alloc(const char *name)
435 struct agent_cfg *cfg;
437 cfg = ao2_alloc_options(sizeof(*cfg), agent_cfg_destructor,
438 AO2_ALLOC_OPT_LOCK_NOLOCK);
439 if (!cfg || ast_string_field_init(cfg, 64)) {
442 ast_string_field_set(cfg, username, name);
446 static void *agent_cfg_find(struct ao2_container *agents, const char *username)
448 return ao2_find(agents, username, OBJ_KEY);
451 /*! Agents configuration */
453 /*! Master configured agents container. */
454 struct ao2_container *agents;
457 static struct aco_type agent_type = {
460 .category_match = ACO_BLACKLIST,
461 .category = "^(general|agents)$",
462 .item_alloc = agent_cfg_alloc,
463 .item_find = agent_cfg_find,
464 .item_offset = offsetof(struct agents_cfg, agents),
467 static struct aco_type *agent_types[] = ACO_TYPES(&agent_type);
469 /* The general category is reserved, but unused */
470 static struct aco_type general_type = {
473 .category_match = ACO_WHITELIST,
474 .category = "^general$",
477 static struct aco_file agents_conf = {
478 .filename = "agents.conf",
479 .types = ACO_TYPES(&general_type, &agent_type),
482 static AO2_GLOBAL_OBJ_STATIC(cfg_handle);
484 static void agents_cfg_destructor(void *vdoomed)
486 struct agents_cfg *doomed = vdoomed;
488 ao2_cleanup(doomed->agents);
489 doomed->agents = NULL;
494 * \brief Create struct agents_cfg object.
497 * \note A lock is not needed for the object or any secondary
498 * created cfg objects. These objects are immutable after the
499 * config is loaded and applied.
501 * \retval New struct agents_cfg object.
502 * \retval NULL on error.
504 static void *agents_cfg_alloc(void)
506 struct agents_cfg *cfg;
508 cfg = ao2_alloc_options(sizeof(*cfg), agents_cfg_destructor,
509 AO2_ALLOC_OPT_LOCK_NOLOCK);
513 cfg->agents = ao2_container_alloc_rbtree(AO2_ALLOC_OPT_LOCK_NOLOCK,
514 AO2_CONTAINER_ALLOC_OPT_DUPS_REJECT, agent_cfg_sort_cmp, NULL);
522 static void agents_post_apply_config(void);
524 CONFIG_INFO_STANDARD(cfg_info, cfg_handle, agents_cfg_alloc,
525 .files = ACO_FILES(&agents_conf),
526 .post_apply_config = agents_post_apply_config,
529 static void destroy_config(void)
531 ao2_global_obj_release(cfg_handle);
532 aco_info_destroy(&cfg_info);
535 static int load_config(void)
537 if (aco_info_init(&cfg_info)) {
542 aco_option_register(&cfg_info, "ackcall", ACO_EXACT, agent_types, "no", OPT_BOOL_T, 1, FLDSET(struct agent_cfg, ack_call));
543 aco_option_register(&cfg_info, "acceptdtmf", ACO_EXACT, agent_types, "#", OPT_STRINGFIELD_T, 1, STRFLDSET(struct agent_cfg, dtmf_accept));
544 aco_option_register(&cfg_info, "autologoff", ACO_EXACT, agent_types, "0", OPT_UINT_T, 0, FLDSET(struct agent_cfg, auto_logoff));
545 aco_option_register(&cfg_info, "wrapuptime", ACO_EXACT, agent_types, "0", OPT_UINT_T, 0, FLDSET(struct agent_cfg, wrapup_time));
546 aco_option_register(&cfg_info, "musiconhold", ACO_EXACT, agent_types, "default", OPT_STRINGFIELD_T, 0, STRFLDSET(struct agent_cfg, moh));
547 aco_option_register(&cfg_info, "recordagentcalls", ACO_EXACT, agent_types, "no", OPT_BOOL_T, 1, FLDSET(struct agent_cfg, record_agent_calls));
548 aco_option_register(&cfg_info, "custom_beep", ACO_EXACT, agent_types, "beep", OPT_STRINGFIELD_T, 1, STRFLDSET(struct agent_cfg, beep_sound));
549 aco_option_register(&cfg_info, "fullname", ACO_EXACT, agent_types, NULL, OPT_STRINGFIELD_T, 0, STRFLDSET(struct agent_cfg, full_name));
551 if (aco_process_config(&cfg_info, 0) == ACO_PROCESS_ERROR) {
563 /*! The agent is defined but an agent is not present. */
564 AGENT_STATE_LOGGED_OUT,
565 /*! Forced initial login wait to allow any local channel optimizations to happen. */
566 AGENT_STATE_PROBATION_WAIT,
567 /*! The agent is ready for a call. */
568 AGENT_STATE_READY_FOR_CALL,
569 /*! The agent has a call waiting to connect. */
570 AGENT_STATE_CALL_PRESENT,
571 /*! The agent needs to ack the call. */
572 AGENT_STATE_CALL_WAIT_ACK,
573 /*! The agent is connected with a call. */
575 /*! The agent is resting between calls. */
576 AGENT_STATE_CALL_WRAPUP,
577 /*! The agent is being kicked out. */
578 AGENT_STATE_LOGGING_OUT,
581 /*! Agent config option override flags. */
582 enum agent_override_flags {
583 AGENT_FLAG_ACK_CALL = (1 << 0),
584 AGENT_FLAG_DTMF_ACCEPT = (1 << 1),
585 AGENT_FLAG_AUTO_LOGOFF = (1 << 2),
586 AGENT_FLAG_WRAPUP_TIME = (1 << 3),
589 /*! \brief Structure representing an agent. */
591 AST_DECLARE_STRING_FIELDS(
592 /*! Identification of the agent. (agents container key) */
593 AST_STRING_FIELD(username);
594 /*! Login override DTMF string for an agent to accept a call. */
595 AST_STRING_FIELD(override_dtmf_accept);
597 /*! Connected line information to send when reentering the holding bridge. */
598 struct ast_party_connected_line waiting_colp;
599 /*! Flags show if settings were overridden by channel vars. */
601 /*! Login override number of seconds for agent to ack a call before being logged off. */
602 unsigned int override_auto_logoff;
603 /*! Login override time after a call in ms before the agent can get a new call. */
604 unsigned int override_wrapup_time;
605 /*! Login override if agent needs to ack a call to accept it. */
606 unsigned int override_ack_call:1;
608 /*! TRUE if the agent is requested to logoff when the current call ends. */
609 unsigned int deferred_logoff:1;
611 /*! Mark and sweep config update to determine if an agent is dead. */
612 unsigned int the_mark:1;
614 * \brief TRUE if the agent is no longer configured and is being destroyed.
616 * \note Agents cannot log in if they are dead.
620 /*! Agent control state variable. */
621 enum agent_state state;
622 /*! Custom device state of agent. */
623 enum ast_device_state devstate;
625 /*! When agent first logged in */
627 /*! When agent login probation started. */
628 time_t probation_start;
629 /*! When call started */
631 /*! When ack timer started */
632 struct timeval ack_time;
633 /*! When last disconnected */
634 struct timeval last_disconnect;
636 /*! Caller is waiting in this bridge for agent to join. (Holds ref) */
637 struct ast_bridge *caller_bridge;
638 /*! Agent is logged in with this channel. (Holds ref) (NULL if not logged in.) */
639 struct ast_channel *logged;
640 /*! Active config values from config file. (Holds ref) */
641 struct agent_cfg *cfg;
644 /*! Container of defined agents. */
645 static struct ao2_container *agents;
648 * \brief Lock the agent.
650 * \param agent Agent to lock
654 #define agent_lock(agent) _agent_lock(agent, __FILE__, __PRETTY_FUNCTION__, __LINE__, #agent)
655 static inline void _agent_lock(struct agent_pvt *agent, const char *file, const char *function, int line, const char *var)
657 __ao2_lock(agent, AO2_LOCK_REQ_MUTEX, file, function, line, var);
661 * \brief Unlock the agent.
663 * \param agent Agent to unlock
667 #define agent_unlock(agent) _agent_unlock(agent, __FILE__, __PRETTY_FUNCTION__, __LINE__, #agent)
668 static inline void _agent_unlock(struct agent_pvt *agent, const char *file, const char *function, int line, const char *var)
670 __ao2_unlock(agent, file, function, line, var);
675 * \brief Obtain the agent logged in channel lock if it exists.
678 * \param agent Pointer to the LOCKED agent_pvt.
680 * \note Assumes the agent lock is already obtained.
682 * \note Defined locking order is channel lock then agent lock.
686 static struct ast_channel *agent_lock_logged(struct agent_pvt *agent)
688 struct ast_channel *logged;
691 if (!agent->logged) { /* No owner. Nothing to do. */
695 /* If we don't ref the logged, it could be killed when we unlock the agent. */
696 logged = ast_channel_ref(agent->logged);
698 /* Locking logged requires us to lock channel, then agent. */
700 ast_channel_lock(logged);
703 /* Check if logged changed during agent unlock period */
704 if (logged != agent->logged) {
705 /* Channel changed. Unref and do another pass. */
706 ast_channel_unlock(logged);
707 ast_channel_unref(logged);
709 /* Channel stayed the same. Return it. */
717 * \brief Get the Agent:agent_id device state.
720 * \param agent_id Username of the agent.
723 * Search the agents container for the agent and return the
726 * \return Device state of the agent.
728 static enum ast_device_state agent_pvt_devstate_get(const char *agent_id)
730 RAII_VAR(struct agent_pvt *, agent, ao2_find(agents, agent_id, OBJ_KEY), ao2_cleanup);
733 return agent->devstate;
735 return AST_DEVICE_INVALID;
740 * \brief Request an agent device state be updated.
743 * \param agent_id Which agent needs the device state updated.
747 static void agent_devstate_changed(const char *agent_id)
749 ast_devstate_changed(AST_DEVICE_UNKNOWN, AST_DEVSTATE_CACHABLE, "Agent:%s", agent_id);
752 static void agent_pvt_destructor(void *vdoomed)
754 struct agent_pvt *doomed = vdoomed;
756 /* Make sure device state reflects agent destruction. */
757 if (!ast_strlen_zero(doomed->username)) {
758 ast_debug(1, "Agent %s: Destroyed.\n", doomed->username);
759 agent_devstate_changed(doomed->username);
762 ast_party_connected_line_free(&doomed->waiting_colp);
763 if (doomed->caller_bridge) {
764 ast_bridge_destroy(doomed->caller_bridge, AST_CAUSE_USER_BUSY);
765 doomed->caller_bridge = NULL;
767 if (doomed->logged) {
768 doomed->logged = ast_channel_unref(doomed->logged);
770 ao2_cleanup(doomed->cfg);
772 ast_string_field_free_memory(doomed);
775 static struct agent_pvt *agent_pvt_new(struct agent_cfg *cfg)
777 struct agent_pvt *agent;
779 agent = ao2_alloc(sizeof(*agent), agent_pvt_destructor);
783 if (ast_string_field_init(agent, 32)) {
787 ast_string_field_set(agent, username, cfg->username);
788 ast_party_connected_line_init(&agent->waiting_colp);
791 agent->devstate = AST_DEVICE_UNAVAILABLE;
797 * \brief Agents ao2 container sort function.
800 * \param obj_left pointer to the (user-defined part) of an object.
801 * \param obj_right pointer to the (user-defined part) of an object.
802 * \param flags flags from ao2_callback()
803 * OBJ_POINTER - if set, 'obj_right', is an object.
804 * OBJ_KEY - if set, 'obj_right', is a search key item that is not an object.
805 * OBJ_PARTIAL_KEY - if set, 'obj_right', is a partial search key item that is not an object.
807 * \retval <0 if obj_left < obj_right
808 * \retval =0 if obj_left == obj_right
809 * \retval >0 if obj_left > obj_right
811 static int agent_pvt_sort_cmp(const void *obj_left, const void *obj_right, int flags)
813 const struct agent_pvt *agent_left = obj_left;
814 const struct agent_pvt *agent_right = obj_right;
815 const char *right_key = obj_right;
818 switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
821 right_key = agent_right->username;
824 cmp = strcmp(agent_left->username, right_key);
826 case OBJ_PARTIAL_KEY:
827 cmp = strncmp(agent_left->username, right_key, strlen(right_key));
835 * \brief ao2_find() callback function.
839 * found = ao2_find(agents, agent, OBJ_POINTER);
840 * found = ao2_find(agents, "agent-id", OBJ_KEY);
841 * found = ao2_find(agents, agent->logged, 0);
843 static int agent_pvt_cmp(void *obj, void *arg, int flags)
845 const struct agent_pvt *agent = obj;
848 switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
851 case OBJ_PARTIAL_KEY:
855 if (agent->logged == arg) {
865 static int agent_mark(void *obj, void *arg, int flags)
867 struct agent_pvt *agent = obj;
875 static void agents_mark(void)
877 ao2_callback(agents, 0, agent_mark, NULL);
880 static int agent_sweep(void *obj, void *arg, int flags)
882 struct agent_pvt *agent = obj;
886 if (agent->the_mark) {
889 /* Unlink dead agents immediately. */
896 static void agents_sweep(void)
898 struct ao2_iterator *iter;
899 struct agent_pvt *agent;
900 struct ast_channel *logged;
902 iter = ao2_callback(agents, OBJ_MULTIPLE | OBJ_UNLINK, agent_sweep, NULL);
906 for (; (agent = ao2_iterator_next(iter)); ao2_ref(agent, -1)) {
909 logged = ast_channel_ref(agent->logged);
918 "Forced logoff of agent %s(%s). Agent no longer configured.\n",
919 agent->username, ast_channel_name(logged));
920 ast_softhangup(logged, AST_SOFTHANGUP_EXPLICIT);
921 ast_channel_unref(logged);
923 ao2_iterator_destroy(iter);
926 static void agents_post_apply_config(void)
928 struct ao2_iterator iter;
929 struct agent_cfg *cfg;
930 RAII_VAR(struct agents_cfg *, cfgs, ao2_global_obj_ref(cfg_handle), ao2_cleanup);
932 ast_assert(cfgs != NULL);
935 iter = ao2_iterator_init(cfgs->agents, 0);
936 for (; (cfg = ao2_iterator_next(&iter)); ao2_ref(cfg, -1)) {
937 RAII_VAR(struct agent_pvt *, agent, ao2_find(agents, cfg->username, OBJ_KEY), ao2_cleanup);
942 if (!agent->logged) {
943 struct agent_cfg *cfg_old;
945 /* Replace the config of agents not logged in. */
946 cfg_old = agent->cfg;
949 ao2_cleanup(cfg_old);
954 agent = agent_pvt_new(cfg);
958 ao2_link(agents, agent);
959 ast_debug(1, "Agent %s: Created.\n", agent->username);
960 agent_devstate_changed(agent->username);
962 ao2_iterator_destroy(&iter);
966 static int agent_logoff_request(const char *agent_id, int soft)
968 struct ast_channel *logged;
969 RAII_VAR(struct agent_pvt *, agent, ao2_find(agents, agent_id, OBJ_KEY), ao2_cleanup);
976 logged = agent_lock_logged(agent);
979 agent->deferred_logoff = 1;
981 ast_softhangup(logged, AST_SOFTHANGUP_EXPLICIT);
983 ast_channel_unlock(logged);
984 ast_channel_unref(logged);
990 /*! Agent holding bridge instance holder. */
991 static AO2_GLOBAL_OBJ_STATIC(agent_holding);
993 /*! Agent holding bridge deferred creation lock. */
994 AST_MUTEX_DEFINE_STATIC(agent_holding_lock);
998 * \brief Connect the agent with the waiting caller.
1001 * \param bridge_channel Agent channel connecting to the caller.
1002 * \param agent Which agent is connecting to the caller.
1004 * \note The agent is locked on entry and not locked on exit.
1008 static void agent_connect_caller(struct ast_bridge_channel *bridge_channel, struct agent_pvt *agent)
1010 struct ast_bridge *caller_bridge;
1011 int record_agent_calls;
1014 record_agent_calls = agent->cfg->record_agent_calls;
1015 caller_bridge = agent->caller_bridge;
1016 agent->caller_bridge = NULL;
1017 agent->state = AGENT_STATE_ON_CALL;
1018 time(&agent->call_start);
1019 agent_unlock(agent);
1021 if (!caller_bridge) {
1023 ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END,
1024 AST_CAUSE_NORMAL_CLEARING);
1027 res = ast_bridge_move(caller_bridge, bridge_channel->bridge, bridge_channel->chan,
1031 ast_bridge_destroy(caller_bridge, AST_CAUSE_USER_BUSY);
1032 ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END,
1033 AST_CAUSE_NORMAL_CLEARING);
1036 ast_bridge_channel_write_control_data(bridge_channel, AST_CONTROL_ANSWER, NULL, 0);
1038 if (record_agent_calls) {
1039 struct ast_bridge_features_automixmonitor options = {
1040 .start_stop = AUTO_MONITOR_START,
1044 * The agent is in the new bridge so we can invoke the
1045 * mixmonitor hook to only start recording.
1047 ast_bridge_features_do(AST_BRIDGE_BUILTIN_AUTOMIXMON, bridge_channel, &options);
1051 static int bridge_agent_hold_ack(struct ast_bridge_channel *bridge_channel, void *hook_pvt)
1053 struct agent_pvt *agent = hook_pvt;
1056 switch (agent->state) {
1057 case AGENT_STATE_CALL_WAIT_ACK:
1058 /* Connect to caller now. */
1059 ast_debug(1, "Agent %s: Acked call.\n", agent->username);
1060 agent_connect_caller(bridge_channel, agent);/* Will unlock agent. */
1065 agent_unlock(agent);
1069 static int bridge_agent_hold_heartbeat(struct ast_bridge_channel *bridge_channel, void *hook_pvt)
1071 struct agent_pvt *agent = hook_pvt;
1072 int probation_timedout = 0;
1073 int ack_timedout = 0;
1074 int wrapup_timedout = 0;
1075 int deferred_logoff;
1076 unsigned int wrapup_time;
1077 unsigned int auto_logoff;
1080 deferred_logoff = agent->deferred_logoff;
1081 if (deferred_logoff) {
1082 agent->state = AGENT_STATE_LOGGING_OUT;
1085 switch (agent->state) {
1086 case AGENT_STATE_PROBATION_WAIT:
1087 probation_timedout =
1088 LOGIN_WAIT_TIMEOUT_TIME <= (time(NULL) - agent->probation_start);
1089 if (probation_timedout) {
1090 /* Now ready for a caller. */
1091 agent->state = AGENT_STATE_READY_FOR_CALL;
1092 agent->devstate = AST_DEVICE_NOT_INUSE;
1095 case AGENT_STATE_CALL_WAIT_ACK:
1096 /* Check ack call time. */
1097 auto_logoff = agent->cfg->auto_logoff;
1098 if (ast_test_flag(agent, AGENT_FLAG_AUTO_LOGOFF)) {
1099 auto_logoff = agent->override_auto_logoff;
1102 auto_logoff *= 1000;
1103 ack_timedout = ast_tvdiff_ms(ast_tvnow(), agent->ack_time) > auto_logoff;
1105 agent->state = AGENT_STATE_LOGGING_OUT;
1109 case AGENT_STATE_CALL_WRAPUP:
1110 /* Check wrapup time. */
1111 wrapup_time = agent->cfg->wrapup_time;
1112 if (ast_test_flag(agent, AGENT_FLAG_WRAPUP_TIME)) {
1113 wrapup_time = agent->override_wrapup_time;
1115 wrapup_timedout = ast_tvdiff_ms(ast_tvnow(), agent->last_disconnect) > wrapup_time;
1116 if (wrapup_timedout) {
1117 agent->state = AGENT_STATE_READY_FOR_CALL;
1118 agent->devstate = AST_DEVICE_NOT_INUSE;
1124 agent_unlock(agent);
1126 if (deferred_logoff) {
1127 ast_debug(1, "Agent %s: Deferred logoff.\n", agent->username);
1128 ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END,
1129 AST_CAUSE_NORMAL_CLEARING);
1130 } else if (probation_timedout) {
1131 ast_debug(1, "Agent %s: Login complete.\n", agent->username);
1132 agent_devstate_changed(agent->username);
1133 } else if (ack_timedout) {
1134 ast_debug(1, "Agent %s: Ack call timeout.\n", agent->username);
1135 ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END,
1136 AST_CAUSE_NORMAL_CLEARING);
1137 } else if (wrapup_timedout) {
1138 ast_debug(1, "Agent %s: Wrapup timeout. Ready for new call.\n", agent->username);
1139 agent_devstate_changed(agent->username);
1145 static void agent_after_bridge_cb(struct ast_channel *chan, void *data);
1146 static void agent_after_bridge_cb_failed(enum ast_bridge_after_cb_reason reason, void *data);
1150 * \brief ast_bridge agent_hold push method.
1153 * \param self Bridge to operate upon.
1154 * \param bridge_channel Bridge channel to push.
1155 * \param swap Bridge channel to swap places with if not NULL.
1157 * \note On entry, self is already locked.
1159 * \retval 0 on success
1160 * \retval -1 on failure
1162 static int bridge_agent_hold_push(struct ast_bridge *self, struct ast_bridge_channel *bridge_channel, struct ast_bridge_channel *swap)
1165 unsigned int wrapup_time;
1166 char dtmf[AST_FEATURE_MAX_LEN];
1167 struct ast_channel *chan;
1168 const char *moh_class;
1169 RAII_VAR(struct agent_pvt *, agent, NULL, ao2_cleanup);
1171 chan = bridge_channel->chan;
1173 agent = ao2_find(agents, swap ? swap->chan : chan, 0);
1175 /* Could not find the agent. */
1179 /* Setup agent entertainment */
1181 moh_class = ast_strdupa(agent->cfg->moh);
1182 agent_unlock(agent);
1183 res |= ast_channel_add_bridge_role(chan, "holding_participant");
1184 res |= ast_channel_set_bridge_role_option(chan, "holding_participant", "idle_mode", "musiconhold");
1185 res |= ast_channel_set_bridge_role_option(chan, "holding_participant", "moh_class", moh_class);
1187 /* Add DTMF acknowledge hook. */
1190 if (ast_test_flag(agent, AGENT_FLAG_ACK_CALL)
1191 ? agent->override_ack_call : agent->cfg->ack_call) {
1192 const char *dtmf_accept;
1194 dtmf_accept = ast_test_flag(agent, AGENT_FLAG_DTMF_ACCEPT)
1195 ? agent->override_dtmf_accept : agent->cfg->dtmf_accept;
1196 ast_copy_string(dtmf, dtmf_accept, sizeof(dtmf));
1198 agent_unlock(agent);
1199 if (!ast_strlen_zero(dtmf)) {
1201 if (ast_bridge_dtmf_hook(bridge_channel->features, dtmf, bridge_agent_hold_ack,
1202 agent, __ao2_cleanup, AST_BRIDGE_HOOK_REMOVE_ON_PULL)) {
1208 /* Add heartbeat interval hook. */
1210 if (ast_bridge_interval_hook(bridge_channel->features, 0, 1000,
1211 bridge_agent_hold_heartbeat, agent, __ao2_cleanup, AST_BRIDGE_HOOK_REMOVE_ON_PULL)) {
1216 res |= ast_bridge_base_v_table.push(self, bridge_channel, swap);
1218 ast_channel_remove_bridge_role(chan, "holding_participant");
1223 res = ast_bridge_set_after_callback(chan, agent_after_bridge_cb,
1224 agent_after_bridge_cb_failed, chan);
1226 ast_channel_remove_bridge_role(chan, "holding_participant");
1231 ast_channel_unref(agent->logged);
1232 agent->logged = ast_channel_ref(chan);
1233 agent_unlock(agent);
1236 * Kick the channel out so it can come back in fully controlled.
1237 * Otherwise, the after bridge callback will linger and the
1238 * agent will have some slightly different behavior in corner
1241 ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END,
1242 AST_CAUSE_NORMAL_CLEARING);
1247 switch (agent->state) {
1248 case AGENT_STATE_LOGGED_OUT:
1250 * \todo XXX the login probation time should be only if it is needed.
1252 * Need to determine if there are any local channels that can
1253 * optimize and wait until they actually do before leaving the
1254 * AGENT_STATE_PROBATION_WAIT state. For now, the blind
1255 * timer of LOGIN_WAIT_TIMEOUT_TIME will do.
1258 * Start the login probation timer.
1260 * We cannot handle an agent local channel optimization when the
1261 * agent is on a call. The optimization may kick the agent
1262 * channel we know about out of the call without our being able
1263 * to switch to the replacement channel. Get any agent local
1264 * channel optimization out of the way while the agent is in the
1267 time(&agent->probation_start);
1268 agent->state = AGENT_STATE_PROBATION_WAIT;
1269 agent_unlock(agent);
1271 case AGENT_STATE_PROBATION_WAIT:
1272 /* Restart the probation timer. */
1273 time(&agent->probation_start);
1274 agent_unlock(agent);
1276 case AGENT_STATE_READY_FOR_CALL:
1278 * Likely someone manually kicked us out of the holding bridge
1279 * and we came right back in.
1281 agent_unlock(agent);
1284 /* Unexpected agent state. */
1287 case AGENT_STATE_CALL_PRESENT:
1288 case AGENT_STATE_CALL_WAIT_ACK:
1289 agent->state = AGENT_STATE_READY_FOR_CALL;
1290 agent->devstate = AST_DEVICE_NOT_INUSE;
1291 agent_unlock(agent);
1292 ast_debug(1, "Agent %s: Call abort recovery complete.\n", agent->username);
1293 agent_devstate_changed(agent->username);
1295 case AGENT_STATE_ON_CALL:
1296 case AGENT_STATE_CALL_WRAPUP:
1297 wrapup_time = agent->cfg->wrapup_time;
1298 if (ast_test_flag(agent, AGENT_FLAG_WRAPUP_TIME)) {
1299 wrapup_time = agent->override_wrapup_time;
1302 agent->state = AGENT_STATE_CALL_WRAPUP;
1304 agent->state = AGENT_STATE_READY_FOR_CALL;
1305 agent->devstate = AST_DEVICE_NOT_INUSE;
1307 agent_unlock(agent);
1309 /* No wrapup time. */
1310 ast_debug(1, "Agent %s: Ready for new call.\n", agent->username);
1311 agent_devstate_changed(agent->username);
1321 * \brief ast_bridge agent_hold pull method.
1323 * \param self Bridge to operate upon.
1324 * \param bridge_channel Bridge channel to pull.
1327 * Remove any channel hooks controlled by the bridge. Release
1328 * any resources held by bridge_channel->bridge_pvt and release
1329 * bridge_channel->bridge_pvt.
1331 * \note On entry, self is already locked.
1335 static void bridge_agent_hold_pull(struct ast_bridge *self, struct ast_bridge_channel *bridge_channel)
1337 ast_channel_remove_bridge_role(bridge_channel->chan, "holding_participant");
1338 ast_bridge_base_v_table.pull(self, bridge_channel);
1342 * \brief The bridge is being dissolved.
1344 * \param self Bridge to operate upon.
1347 * The bridge is being dissolved. Remove any external
1348 * references to the bridge so it can be destroyed.
1350 * \note On entry, self must NOT be locked.
1354 static void bridge_agent_hold_dissolving(struct ast_bridge *self)
1356 ao2_global_obj_release(agent_holding);
1357 ast_bridge_base_v_table.dissolving(self);
1360 static struct ast_bridge_methods bridge_agent_hold_v_table;
1362 static struct ast_bridge *bridge_agent_hold_new(void)
1364 struct ast_bridge *bridge;
1366 bridge = bridge_alloc(sizeof(struct ast_bridge), &bridge_agent_hold_v_table);
1367 bridge = bridge_base_init(bridge, AST_BRIDGE_CAPABILITY_HOLDING,
1368 AST_BRIDGE_FLAG_MERGE_INHIBIT_TO | AST_BRIDGE_FLAG_MERGE_INHIBIT_FROM
1369 | AST_BRIDGE_FLAG_SWAP_INHIBIT_FROM | AST_BRIDGE_FLAG_TRANSFER_PROHIBITED,
1371 bridge = bridge_register(bridge);
1375 static void bridge_init_agent_hold(void)
1377 /* Setup bridge agent_hold subclass v_table. */
1378 bridge_agent_hold_v_table = ast_bridge_base_v_table;
1379 bridge_agent_hold_v_table.name = "agent_hold";
1380 bridge_agent_hold_v_table.dissolving = bridge_agent_hold_dissolving;
1381 bridge_agent_hold_v_table.push = bridge_agent_hold_push;
1382 bridge_agent_hold_v_table.pull = bridge_agent_hold_pull;
1385 static int bridge_agent_hold_deferred_create(void)
1387 RAII_VAR(struct ast_bridge *, holding, ao2_global_obj_ref(agent_holding), ao2_cleanup);
1390 ast_mutex_lock(&agent_holding_lock);
1391 holding = ao2_global_obj_ref(agent_holding);
1393 holding = bridge_agent_hold_new();
1394 ao2_global_obj_replace_unref(agent_holding, holding);
1396 ast_mutex_unlock(&agent_holding_lock);
1398 ast_log(LOG_ERROR, "Could not create agent holding bridge.\n");
1405 static void send_agent_login(struct ast_channel *chan, const char *agent)
1407 RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
1409 ast_assert(agent != NULL);
1411 blob = ast_json_pack("{s: s}",
1417 ast_channel_publish_blob(chan, ast_channel_agent_login_type(), blob);
1420 static void send_agent_logoff(struct ast_channel *chan, const char *agent, long logintime)
1422 RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
1424 ast_assert(agent != NULL);
1426 blob = ast_json_pack("{s: s, s: i}",
1428 "logintime", logintime);
1433 ast_channel_publish_blob(chan, ast_channel_agent_logoff_type(), blob);
1438 * \brief Logout the agent.
1441 * \param agent Which agent logging out.
1443 * \note On entry agent is already locked. On exit it is no longer locked.
1447 static void agent_logout(struct agent_pvt *agent)
1449 struct ast_channel *logged;
1450 struct ast_bridge *caller_bridge;
1451 long time_logged_in;
1453 time_logged_in = time(NULL) - agent->login_start;
1454 logged = agent->logged;
1455 agent->logged = NULL;
1456 caller_bridge = agent->caller_bridge;
1457 agent->caller_bridge = NULL;
1458 agent->state = AGENT_STATE_LOGGED_OUT;
1459 agent->devstate = AST_DEVICE_UNAVAILABLE;
1460 ast_clear_flag(agent, AST_FLAGS_ALL);
1461 agent_unlock(agent);
1462 agent_devstate_changed(agent->username);
1464 if (caller_bridge) {
1465 ast_bridge_destroy(caller_bridge, AST_CAUSE_USER_BUSY);
1468 ast_channel_lock(logged);
1469 send_agent_logoff(logged, agent->username, time_logged_in);
1470 ast_channel_unlock(logged);
1471 ast_verb(2, "Agent '%s' logged out. Logged in for %ld seconds.\n",
1472 agent->username, time_logged_in);
1473 ast_channel_unref(logged);
1478 * \brief Agent driver loop.
1481 * \param agent Which agent.
1482 * \param logged The logged in channel.
1486 static void agent_run(struct agent_pvt *agent, struct ast_channel *logged)
1488 struct ast_bridge_features features;
1490 if (ast_bridge_features_init(&features)) {
1491 ast_channel_hangupcause_set(logged, AST_CAUSE_NORMAL_CLEARING);
1492 goto agent_run_cleanup;
1495 struct agents_cfg *cfgs;
1496 struct agent_cfg *cfg_new;
1497 struct agent_cfg *cfg_old;
1498 struct ast_bridge *holding;
1499 struct ast_bridge *caller_bridge;
1501 ast_channel_hangupcause_set(logged, AST_CAUSE_NORMAL_CLEARING);
1503 holding = ao2_global_obj_ref(agent_holding);
1505 ast_debug(1, "Agent %s: Someone destroyed the agent holding bridge.\n",
1511 * When the agent channel leaves the bridging system we usually
1512 * want to put the agent back into the holding bridge for the
1515 ast_bridge_join(holding, logged, NULL, &features, NULL,
1516 AST_BRIDGE_JOIN_PASS_REFERENCE);
1517 if (logged != agent->logged) {
1518 /* This channel is no longer the logged in agent. */
1523 /* The agent is no longer configured. */
1527 /* Update the agent's config before rejoining the holding bridge. */
1528 cfgs = ao2_global_obj_ref(cfg_handle);
1530 /* There is no agent configuration. All agents were destroyed. */
1533 cfg_new = ao2_find(cfgs->agents, agent->username, OBJ_KEY);
1536 /* The agent is no longer configured. */
1540 cfg_old = agent->cfg;
1541 agent->cfg = cfg_new;
1543 agent->last_disconnect = ast_tvnow();
1545 /* Clear out any caller bridge before rejoining the holding bridge. */
1546 caller_bridge = agent->caller_bridge;
1547 agent->caller_bridge = NULL;
1548 agent_unlock(agent);
1549 ao2_ref(cfg_old, -1);
1550 if (caller_bridge) {
1551 ast_bridge_destroy(caller_bridge, AST_CAUSE_USER_BUSY);
1554 if (agent->state == AGENT_STATE_LOGGING_OUT
1555 || agent->deferred_logoff
1556 || ast_check_hangup_locked(logged)) {
1557 /* The agent was requested to logout or hungup. */
1562 * It is safe to access agent->waiting_colp without a lock. It
1563 * is only setup on agent login and not changed.
1565 ast_channel_update_connected_line(logged, &agent->waiting_colp, NULL);
1567 ast_bridge_features_cleanup(&features);
1571 if (logged != agent->logged) {
1573 * We are no longer the agent channel because of local channel
1576 agent_unlock(agent);
1577 ast_debug(1, "Agent %s: Channel %s is no longer the agent.\n",
1578 agent->username, ast_channel_name(logged));
1581 agent_logout(agent);
1584 static void agent_after_bridge_cb(struct ast_channel *chan, void *data)
1586 struct agent_pvt *agent;
1588 agent = ao2_find(agents, chan, 0);
1593 ast_debug(1, "Agent %s: New agent channel %s.\n",
1594 agent->username, ast_channel_name(chan));
1595 agent_run(agent, chan);
1599 static void agent_after_bridge_cb_failed(enum ast_bridge_after_cb_reason reason, void *data)
1601 struct ast_channel *chan = data;
1602 struct agent_pvt *agent;
1604 agent = ao2_find(agents, chan, 0);
1608 ast_log(LOG_WARNING, "Agent %s: Forced logout. Lost control of %s because: %s\n",
1609 agent->username, ast_channel_name(chan),
1610 ast_bridge_after_cb_reason_string(reason));
1612 agent_logout(agent);
1618 * \brief Get the lock on the agent bridge_channel and return it.
1621 * \param agent Whose bridge_channel to get.
1623 * \retval bridge_channel on success (Reffed and locked).
1624 * \retval NULL on error.
1626 static struct ast_bridge_channel *agent_bridge_channel_get_lock(struct agent_pvt *agent)
1628 struct ast_channel *logged;
1629 struct ast_bridge_channel *bc;
1633 logged = agent->logged;
1635 agent_unlock(agent);
1638 ast_channel_ref(logged);
1639 agent_unlock(agent);
1641 ast_channel_lock(logged);
1642 bc = ast_channel_get_bridge_channel(logged);
1643 ast_channel_unlock(logged);
1644 ast_channel_unref(logged);
1646 if (agent->logged != logged) {
1652 ast_bridge_channel_lock(bc);
1653 if (bc->chan != logged || agent->logged != logged) {
1654 ast_bridge_channel_unlock(bc);
1662 static void caller_abort_agent(struct agent_pvt *agent)
1664 struct ast_bridge_channel *logged;
1666 logged = agent_bridge_channel_get_lock(agent);
1668 struct ast_bridge *caller_bridge;
1670 ast_debug(1, "Agent '%s' no longer logged in.\n", agent->username);
1673 caller_bridge = agent->caller_bridge;
1674 agent->caller_bridge = NULL;
1675 agent_unlock(agent);
1676 if (caller_bridge) {
1677 ast_bridge_destroy(caller_bridge, AST_CAUSE_USER_BUSY);
1682 /* Kick the agent out of the holding bridge to reset it. */
1683 ast_bridge_channel_leave_bridge_nolock(logged, BRIDGE_CHANNEL_STATE_END,
1684 AST_CAUSE_NORMAL_CLEARING);
1685 ast_bridge_channel_unlock(logged);
1688 static int caller_safety_timeout(struct ast_bridge_channel *bridge_channel, void *hook_pvt)
1690 struct agent_pvt *agent = hook_pvt;
1692 if (agent->state == AGENT_STATE_CALL_PRESENT) {
1693 ast_verb(3, "Agent '%s' did not respond. Safety timeout.\n", agent->username);
1694 ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END,
1695 AST_CAUSE_USER_BUSY);
1696 caller_abort_agent(agent);
1702 static void agent_alert(struct ast_bridge_channel *bridge_channel, const void *payload, size_t payload_size)
1704 const char *agent_id = payload;
1705 const char *playfile;
1706 RAII_VAR(struct agent_pvt *, agent, NULL, ao2_cleanup);
1708 agent = ao2_find(agents, agent_id, OBJ_KEY);
1710 ast_debug(1, "Agent '%s' does not exist. Where did it go?\n", agent_id);
1714 /* Change holding bridge participant role's idle mode to silence */
1715 ast_bridge_channel_lock_bridge(bridge_channel);
1716 ast_bridge_channel_clear_roles(bridge_channel);
1717 ast_channel_set_bridge_role_option(bridge_channel->chan, "holding_participant", "idle_mode", "silence");
1718 ast_bridge_channel_establish_roles(bridge_channel);
1719 ast_bridge_unlock(bridge_channel->bridge);
1721 /* Alert the agent. */
1723 playfile = ast_strdupa(agent->cfg->beep_sound);
1724 agent_unlock(agent);
1725 ast_stream_and_wait(bridge_channel->chan, playfile, AST_DIGIT_NONE);
1728 switch (agent->state) {
1729 case AGENT_STATE_CALL_PRESENT:
1730 if (ast_test_flag(agent, AGENT_FLAG_ACK_CALL)
1731 ? agent->override_ack_call : agent->cfg->ack_call) {
1732 agent->state = AGENT_STATE_CALL_WAIT_ACK;
1733 agent->ack_time = ast_tvnow();
1737 /* Connect to caller now. */
1738 ast_debug(1, "Agent %s: Immediately connecting to call.\n", agent->username);
1739 agent_connect_caller(bridge_channel, agent);/* Will unlock agent. */
1744 agent_unlock(agent);
1747 static int send_alert_to_agent(struct ast_bridge_channel *bridge_channel, const char *agent_id)
1749 return ast_bridge_channel_queue_callback(bridge_channel,
1750 AST_BRIDGE_CHANNEL_CB_OPTION_MEDIA, agent_alert, agent_id, strlen(agent_id) + 1);
1753 static int send_colp_to_agent(struct ast_bridge_channel *bridge_channel, struct ast_party_connected_line *connected)
1755 struct ast_set_party_connected_line update = {
1760 unsigned char data[1024]; /* This should be large enough */
1763 datalen = ast_connected_line_build_data(data, sizeof(data), connected, &update);
1764 if (datalen == (size_t) -1) {
1768 return ast_bridge_channel_queue_control_data(bridge_channel,
1769 AST_CONTROL_CONNECTED_LINE, data, datalen);
1773 * \brief Dialplan AgentRequest application to locate an agent to talk with.
1775 * \param chan Channel wanting to talk with an agent.
1776 * \param data Application parameters
1778 * \retval 0 To continue in dialplan.
1779 * \retval -1 To hangup.
1781 static int agent_request_exec(struct ast_channel *chan, const char *data)
1783 struct ast_bridge *caller_bridge;
1784 struct ast_bridge_channel *logged;
1787 struct ast_bridge_features caller_features;
1788 struct ast_party_connected_line connected;
1789 AST_DECLARE_APP_ARGS(args,
1790 AST_APP_ARG(agent_id);
1791 AST_APP_ARG(other); /* Any remaining unused arguments */
1794 RAII_VAR(struct agent_pvt *, agent, NULL, ao2_cleanup);
1796 if (bridge_agent_hold_deferred_create()) {
1800 parse = ast_strdupa(data);
1801 AST_STANDARD_APP_ARGS(args, parse);
1803 if (ast_strlen_zero(args.agent_id)) {
1804 ast_log(LOG_WARNING, "AgentRequest requires an AgentId\n");
1808 /* Find the agent. */
1809 agent = ao2_find(agents, args.agent_id, OBJ_KEY);
1811 ast_verb(3, "Agent '%s' does not exist.\n", args.agent_id);
1812 pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "INVALID");
1816 if (ast_bridge_features_init(&caller_features)) {
1820 /* Add safety timeout hook. */
1822 if (ast_bridge_interval_hook(&caller_features, 0, CALLER_SAFETY_TIMEOUT_TIME,
1823 caller_safety_timeout, agent, __ao2_cleanup, AST_BRIDGE_HOOK_REMOVE_ON_PULL)) {
1825 ast_bridge_features_cleanup(&caller_features);
1829 caller_bridge = ast_bridge_basic_new();
1830 if (!caller_bridge) {
1831 ast_bridge_features_cleanup(&caller_features);
1835 /* Get COLP for agent. */
1836 ast_party_connected_line_init(&connected);
1837 ast_channel_lock(chan);
1838 ast_connected_line_copy_from_caller(&connected, ast_channel_caller(chan));
1839 ast_channel_unlock(chan);
1842 switch (agent->state) {
1843 case AGENT_STATE_LOGGED_OUT:
1844 case AGENT_STATE_LOGGING_OUT:
1845 agent_unlock(agent);
1846 ast_party_connected_line_free(&connected);
1847 ast_bridge_destroy(caller_bridge, 0);
1848 ast_bridge_features_cleanup(&caller_features);
1849 ast_verb(3, "Agent '%s' not logged in.\n", agent->username);
1850 pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "NOT_LOGGED_IN");
1852 case AGENT_STATE_READY_FOR_CALL:
1853 ao2_ref(caller_bridge, +1);
1854 agent->caller_bridge = caller_bridge;
1855 agent->state = AGENT_STATE_CALL_PRESENT;
1856 agent->devstate = AST_DEVICE_INUSE;
1859 agent_unlock(agent);
1860 ast_party_connected_line_free(&connected);
1861 ast_bridge_destroy(caller_bridge, 0);
1862 ast_bridge_features_cleanup(&caller_features);
1863 ast_verb(3, "Agent '%s' is busy.\n", agent->username);
1864 pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "BUSY");
1867 agent_unlock(agent);
1868 agent_devstate_changed(agent->username);
1870 logged = agent_bridge_channel_get_lock(agent);
1872 ast_party_connected_line_free(&connected);
1873 ast_bridge_destroy(caller_bridge, 0);
1874 ast_bridge_features_cleanup(&caller_features);
1875 ast_verb(3, "Agent '%s' not logged in.\n", agent->username);
1876 pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "NOT_LOGGED_IN");
1877 caller_abort_agent(agent);
1881 send_colp_to_agent(logged, &connected);
1882 ast_party_connected_line_free(&connected);
1884 res = send_alert_to_agent(logged, agent->username);
1885 ast_bridge_channel_unlock(logged);
1886 ao2_ref(logged, -1);
1888 ast_bridge_destroy(caller_bridge, 0);
1889 ast_bridge_features_cleanup(&caller_features);
1890 ast_verb(3, "Agent '%s': Failed to alert the agent.\n", agent->username);
1891 pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "ERROR");
1892 caller_abort_agent(agent);
1896 ast_indicate(chan, AST_CONTROL_RINGING);
1897 ast_bridge_join(caller_bridge, chan, NULL, &caller_features, NULL,
1898 AST_BRIDGE_JOIN_PASS_REFERENCE);
1899 ast_bridge_features_cleanup(&caller_features);
1906 * \brief Get agent config values from the login channel.
1909 * \param agent What to setup channel config values on.
1910 * \param chan Channel logging in as an agent.
1914 static void agent_login_channel_config(struct agent_pvt *agent, struct ast_channel *chan)
1916 struct ast_flags opts = { 0 };
1917 struct ast_party_connected_line connected;
1918 unsigned int override_ack_call = 0;
1919 unsigned int override_auto_logoff = 0;
1920 unsigned int override_wrapup_time = 0;
1921 const char *override_dtmf_accept = NULL;
1924 ast_party_connected_line_init(&connected);
1926 /* Get config values from channel. */
1927 ast_channel_lock(chan);
1928 ast_party_connected_line_copy(&connected, ast_channel_connected(chan));
1930 var = pbx_builtin_getvar_helper(chan, "AGENTACKCALL");
1931 if (!ast_strlen_zero(var)) {
1932 override_ack_call = ast_true(var) ? 1 : 0;
1933 ast_set_flag(&opts, AGENT_FLAG_ACK_CALL);
1936 var = pbx_builtin_getvar_helper(chan, "AGENTACCEPTDTMF");
1937 if (!ast_strlen_zero(var)) {
1938 override_dtmf_accept = ast_strdupa(var);
1939 ast_set_flag(&opts, AGENT_FLAG_DTMF_ACCEPT);
1942 var = pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF");
1943 if (!ast_strlen_zero(var)) {
1944 if (sscanf(var, "%u", &override_auto_logoff) == 1) {
1945 ast_set_flag(&opts, AGENT_FLAG_AUTO_LOGOFF);
1949 var = pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME");
1950 if (!ast_strlen_zero(var)) {
1951 if (sscanf(var, "%u", &override_wrapup_time) == 1) {
1952 ast_set_flag(&opts, AGENT_FLAG_WRAPUP_TIME);
1955 ast_channel_unlock(chan);
1957 /* Set config values on agent. */
1959 ast_party_connected_line_free(&agent->waiting_colp);
1960 agent->waiting_colp = connected;
1962 ast_string_field_set(agent, override_dtmf_accept, override_dtmf_accept);
1963 ast_copy_flags(agent, &opts, AST_FLAGS_ALL);
1964 agent->override_auto_logoff = override_auto_logoff;
1965 agent->override_wrapup_time = override_wrapup_time;
1966 agent->override_ack_call = override_ack_call;
1967 agent_unlock(agent);
1970 enum AGENT_LOGIN_OPT_FLAGS {
1971 OPT_SILENT = (1 << 0),
1973 AST_APP_OPTIONS(agent_login_opts, BEGIN_OPTIONS
1974 AST_APP_OPTION('s', OPT_SILENT),
1978 * \brief Dialplan AgentLogin application to log in an agent.
1980 * \param chan Channel attempting to login as an agent.
1981 * \param data Application parameters
1983 * \retval 0 To continue in dialplan.
1984 * \retval -1 To hangup.
1986 static int agent_login_exec(struct ast_channel *chan, const char *data)
1989 struct ast_flags opts;
1990 AST_DECLARE_APP_ARGS(args,
1991 AST_APP_ARG(agent_id);
1992 AST_APP_ARG(options);
1993 AST_APP_ARG(other); /* Any remaining unused arguments */
1996 RAII_VAR(struct agent_pvt *, agent, NULL, ao2_cleanup);
1998 if (bridge_agent_hold_deferred_create()) {
2002 if (ast_channel_state(chan) != AST_STATE_UP && ast_answer(chan)) {
2006 parse = ast_strdupa(data);
2007 AST_STANDARD_APP_ARGS(args, parse);
2009 if (ast_strlen_zero(args.agent_id)) {
2010 ast_log(LOG_WARNING, "AgentLogin requires an AgentId\n");
2014 if (ast_app_parse_options(agent_login_opts, &opts, NULL, args.options)) {
2015 /* General invalid option syntax. */
2019 /* Find the agent. */
2020 agent = ao2_find(agents, args.agent_id, OBJ_KEY);
2022 ast_verb(3, "Agent '%s' does not exist.\n", args.agent_id);
2023 pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "INVALID");
2027 /* Has someone already logged in as this agent already? */
2029 if (agent->logged) {
2030 agent_unlock(agent);
2031 ast_verb(3, "Agent '%s' already logged in.\n", agent->username);
2032 pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "ALREADY_LOGGED_IN");
2035 agent->logged = ast_channel_ref(chan);
2036 agent->last_disconnect = ast_tvnow();
2037 time(&agent->login_start);
2038 agent->deferred_logoff = 0;
2039 agent_unlock(agent);
2041 agent_login_channel_config(agent, chan);
2043 if (!ast_test_flag(&opts, OPT_SILENT)
2044 && !ast_streamfile(chan, "agent-loginok", ast_channel_language(chan))) {
2045 ast_waitstream(chan, "");
2048 ast_verb(2, "Agent '%s' logged in (format %s/%s)\n", agent->username,
2049 ast_getformatname(ast_channel_readformat(chan)),
2050 ast_getformatname(ast_channel_writeformat(chan)));
2051 ast_channel_lock(chan);
2052 send_agent_login(chan, agent->username);
2053 ast_channel_unlock(chan);
2055 agent_run(agent, chan);
2059 static int agent_function_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
2062 struct agent_pvt *agent;
2063 struct ast_channel *logged;
2064 AST_DECLARE_APP_ARGS(args,
2065 AST_APP_ARG(agentid);
2071 parse = ast_strdupa(data ?: "");
2072 AST_NONSTANDARD_APP_ARGS(args, parse, ':');
2074 if (ast_strlen_zero(args.agentid)) {
2075 ast_log(LOG_WARNING, "The AGENT function requires an argument - agentid!\n");
2079 args.item = "status";
2082 agent = ao2_find(agents, args.agentid, OBJ_KEY);
2084 ast_log(LOG_WARNING, "Agent '%s' not found!\n", args.agentid);
2089 if (!strcasecmp(args.item, "status")) {
2092 if (agent->logged) {
2093 status = "LOGGEDIN";
2095 status = "LOGGEDOUT";
2097 ast_copy_string(buf, status, len);
2098 } else if (!strcasecmp(args.item, "name")) {
2099 ast_copy_string(buf, agent->cfg->full_name, len);
2100 } else if (!strcasecmp(args.item, "mohclass")) {
2101 ast_copy_string(buf, agent->cfg->moh, len);
2102 } else if (!strcasecmp(args.item, "channel")) {
2103 logged = agent_lock_logged(agent);
2107 ast_copy_string(buf, ast_channel_name(logged), len);
2108 ast_channel_unlock(logged);
2109 ast_channel_unref(logged);
2111 pos = strrchr(buf, '-');
2116 } else if (!strcasecmp(args.item, "fullchannel")) {
2117 logged = agent_lock_logged(agent);
2119 ast_copy_string(buf, ast_channel_name(logged), len);
2120 ast_channel_unlock(logged);
2121 ast_channel_unref(logged);
2124 agent_unlock(agent);
2130 static struct ast_custom_function agent_function = {
2132 .read = agent_function_read,
2135 struct agent_complete {
2136 /*! Nth match to return. */
2138 /*! Which match currently on. */
2142 static int complete_agent_search(void *obj, void *arg, void *data, int flags)
2144 struct agent_complete *search = data;
2146 if (++search->which > search->state) {
2152 static char *complete_agent(const char *word, int state)
2155 struct agent_pvt *agent;
2156 struct agent_complete search = {
2160 agent = ao2_callback_data(agents, ast_strlen_zero(word) ? 0 : OBJ_PARTIAL_KEY,
2161 complete_agent_search, (char *) word, &search);
2165 ret = ast_strdup(agent->username);
2170 static int complete_agent_logoff_search(void *obj, void *arg, void *data, int flags)
2172 struct agent_pvt *agent = obj;
2173 struct agent_complete *search = data;
2175 if (!agent->logged) {
2178 if (++search->which > search->state) {
2184 static char *complete_agent_logoff(const char *word, int state)
2187 struct agent_pvt *agent;
2188 struct agent_complete search = {
2192 agent = ao2_callback_data(agents, ast_strlen_zero(word) ? 0 : OBJ_PARTIAL_KEY,
2193 complete_agent_logoff_search, (char *) word, &search);
2197 ret = ast_strdup(agent->username);
2202 static void agent_show_requested(struct ast_cli_args *a, int online_only)
2204 #define FORMAT_HDR "%-8s %-20s %-11s %-30s %s\n"
2205 #define FORMAT_ROW "%-8s %-20s %-11s %-30s %s\n"
2207 struct ao2_iterator iter;
2208 struct agent_pvt *agent;
2209 struct ast_str *out = ast_str_alloca(512);
2210 unsigned int agents_total = 0;
2211 unsigned int agents_logged_in = 0;
2212 unsigned int agents_talking = 0;
2214 ast_cli(a->fd, FORMAT_HDR, "Agent-ID", "Name", "State", "Channel", "Talking with");
2215 iter = ao2_iterator_init(agents, 0);
2216 for (; (agent = ao2_iterator_next(&iter)); ao2_ref(agent, -1)) {
2217 struct ast_channel *logged;
2222 logged = agent_lock_logged(agent);
2224 const char *talking_with;
2228 talking_with = pbx_builtin_getvar_helper(logged, "BRIDGEPEER");
2229 if (!ast_strlen_zero(talking_with)) {
2234 ast_str_set(&out, 0, FORMAT_ROW, agent->username, agent->cfg->full_name,
2235 ast_devstate_str(agent->devstate), ast_channel_name(logged), talking_with);
2236 ast_channel_unlock(logged);
2237 ast_channel_unref(logged);
2239 ast_str_set(&out, 0, FORMAT_ROW, agent->username, agent->cfg->full_name,
2240 ast_devstate_str(agent->devstate), "", "");
2242 agent_unlock(agent);
2244 if (!online_only || logged) {
2245 ast_cli(a->fd, "%s", ast_str_buffer(out));
2248 ao2_iterator_destroy(&iter);
2250 ast_cli(a->fd, "\nDefined agents: %u, Logged in: %u, Talking: %u\n",
2251 agents_total, agents_logged_in, agents_talking);
2257 static char *agent_handle_show_online(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2261 e->command = "agent show online";
2263 "Usage: agent show online\n"
2264 " Provides summary information for logged in agents.\n";
2271 return CLI_SHOWUSAGE;
2274 agent_show_requested(a, 1);
2279 static char *agent_handle_show_all(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2283 e->command = "agent show all";
2285 "Usage: agent show all\n"
2286 " Provides summary information for all agents.\n";
2293 return CLI_SHOWUSAGE;
2296 agent_show_requested(a, 0);
2301 static char *agent_handle_show_specific(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2303 struct agent_pvt *agent;
2304 struct ast_channel *logged;
2305 struct ast_str *out = ast_str_alloca(4096);
2309 e->command = "agent show";
2311 "Usage: agent show <agent-id>\n"
2312 " Show information about the <agent-id> agent\n";
2316 return complete_agent(a->word, a->n);
2322 return CLI_SHOWUSAGE;
2325 agent = ao2_find(agents, a->argv[2], OBJ_KEY);
2327 ast_cli(a->fd, "Agent '%s' not found\n", a->argv[2]);
2332 logged = agent_lock_logged(agent);
2333 ast_str_set(&out, 0, "Id: %s\n", agent->username);
2334 ast_str_append(&out, 0, "Name: %s\n", agent->cfg->full_name);
2335 ast_str_append(&out, 0, "Beep: %s\n", agent->cfg->beep_sound);
2336 ast_str_append(&out, 0, "MOH: %s\n", agent->cfg->moh);
2337 ast_str_append(&out, 0, "RecordCalls: %s\n", AST_CLI_YESNO(agent->cfg->record_agent_calls));
2338 ast_str_append(&out, 0, "State: %s\n", ast_devstate_str(agent->devstate));
2340 const char *talking_with;
2342 ast_str_append(&out, 0, "LoggedInChannel: %s\n", ast_channel_name(logged));
2343 ast_str_append(&out, 0, "LoggedInTime: %ld\n", (long) agent->login_start);
2344 talking_with = pbx_builtin_getvar_helper(logged, "BRIDGEPEER");
2345 if (!ast_strlen_zero(talking_with)) {
2346 ast_str_append(&out, 0, "TalkingWith: %s\n", talking_with);
2347 ast_str_append(&out, 0, "CallStarted: %ld\n", (long) agent->call_start);
2349 ast_channel_unlock(logged);
2350 ast_channel_unref(logged);
2352 agent_unlock(agent);
2355 ast_cli(a->fd, "%s", ast_str_buffer(out));
2360 static char *agent_handle_logoff_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2364 e->command = "agent logoff";
2366 "Usage: agent logoff <agent-id> [soft]\n"
2367 " Sets an agent as no longer logged in.\n"
2368 " If 'soft' is specified, do not hangup existing calls.\n";
2372 return complete_agent_logoff(a->word, a->n);
2373 } else if (a->pos == 3 && a->n == 0
2374 && (ast_strlen_zero(a->word)
2375 || !strncasecmp("soft", a->word, strlen(a->word)))) {
2376 return ast_strdup("soft");
2381 if (a->argc < 3 || 4 < a->argc) {
2382 return CLI_SHOWUSAGE;
2384 if (a->argc == 4 && strcasecmp(a->argv[3], "soft")) {
2385 return CLI_SHOWUSAGE;
2388 if (!agent_logoff_request(a->argv[2], a->argc == 4)) {
2389 ast_cli(a->fd, "Logging out %s\n", a->argv[2]);
2395 static struct ast_cli_entry cli_agents[] = {
2396 AST_CLI_DEFINE(agent_handle_show_online, "Show status of online agents"),
2397 AST_CLI_DEFINE(agent_handle_show_all, "Show status of all agents"),
2398 AST_CLI_DEFINE(agent_handle_show_specific, "Show information about an agent"),
2399 AST_CLI_DEFINE(agent_handle_logoff_cmd, "Sets an agent offline"),
2402 static int action_agents(struct mansession *s, const struct message *m)
2404 const char *id = astman_get_header(m, "ActionID");
2405 char id_text[AST_MAX_BUF];
2406 struct ao2_iterator iter;
2407 struct agent_pvt *agent;
2408 struct ast_str *out = ast_str_alloca(4096);
2410 if (!ast_strlen_zero(id)) {
2411 snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", id);
2415 astman_send_ack(s, m, "Agents will follow");
2417 iter = ao2_iterator_init(agents, 0);
2418 for (; (agent = ao2_iterator_next(&iter)); ao2_ref(agent, -1)) {
2419 struct ast_channel *logged;
2422 logged = agent_lock_logged(agent);
2426 * AGENT_LOGGEDOFF - Agent isn't logged in
2427 * AGENT_IDLE - Agent is logged in, and waiting for call
2428 * AGENT_ONCALL - Agent is logged in, and on a call
2429 * AGENT_UNKNOWN - Don't know anything about agent. Shouldn't ever get this.
2431 ast_str_set(&out, 0, "Agent: %s\r\n", agent->username);
2432 ast_str_append(&out, 0, "Name: %s\r\n", agent->cfg->full_name);
2435 const char *talking_to_chan;
2436 struct ast_str *logged_headers;
2437 RAII_VAR(struct ast_channel_snapshot *, logged_snapshot, ast_channel_snapshot_create(logged), ao2_cleanup);
2439 if (!logged_snapshot
2440 || !(logged_headers =
2441 ast_manager_build_channel_state_string(logged_snapshot))) {
2442 ast_channel_unlock(logged);
2443 ast_channel_unref(logged);
2444 agent_unlock(agent);
2448 talking_to_chan = pbx_builtin_getvar_helper(logged, "BRIDGEPEER");
2449 if (!ast_strlen_zero(talking_to_chan)) {
2450 ast_str_append(&out, 0, "Status: %s\r\n", "AGENT_ONCALL");
2451 ast_str_append(&out, 0, "TalkingToChan: %s\r\n", talking_to_chan);
2452 ast_str_append(&out, 0, "CallStarted: %ld\n", (long) agent->call_start);
2454 ast_str_append(&out, 0, "Status: %s\r\n", "AGENT_IDLE");
2456 ast_str_append(&out, 0, "LoggedInTime: %ld\r\n", (long) agent->login_start);
2457 ast_str_append(&out, 0, "%s", ast_str_buffer(logged_headers));
2458 ast_channel_unlock(logged);
2459 ast_channel_unref(logged);
2460 ast_free(logged_headers);
2462 ast_str_append(&out, 0, "Status: %s\r\n", "AGENT_LOGGEDOFF");
2465 agent_unlock(agent);
2467 astman_append(s, "Event: Agents\r\n"
2469 ast_str_buffer(out), id_text);
2471 ao2_iterator_destroy(&iter);
2473 astman_append(s, "Event: AgentsComplete\r\n"
2479 static int action_agent_logoff(struct mansession *s, const struct message *m)
2481 const char *agent = astman_get_header(m, "Agent");
2482 const char *soft_s = astman_get_header(m, "Soft"); /* "true" is don't hangup */
2484 if (ast_strlen_zero(agent)) {
2485 astman_send_error(s, m, "No agent specified");
2489 if (!agent_logoff_request(agent, ast_true(soft_s))) {
2490 astman_send_ack(s, m, "Agent logged out");
2492 astman_send_error(s, m, "No such agent");
2498 static int unload_module(void)
2500 struct ast_bridge *holding;
2502 /* Unregister dialplan applications */
2503 ast_unregister_application(app_agent_login);
2504 ast_unregister_application(app_agent_request);
2506 /* Unregister dialplan functions */
2507 ast_custom_function_unregister(&agent_function);
2509 /* Unregister manager command */
2510 ast_manager_unregister("Agents");
2511 ast_manager_unregister("AgentLogoff");
2513 /* Unregister CLI commands */
2514 ast_cli_unregister_multiple(cli_agents, ARRAY_LEN(cli_agents));
2516 ast_devstate_prov_del("Agent");
2518 /* Destroy agent holding bridge. */
2519 holding = ao2_global_obj_replace(agent_holding, NULL);
2521 ast_bridge_destroy(holding, 0);
2525 ao2_ref(agents, -1);
2530 static int load_module(void)
2534 agents = ao2_container_alloc_rbtree(AO2_ALLOC_OPT_LOCK_MUTEX,
2535 AO2_CONTAINER_ALLOC_OPT_DUPS_REPLACE, agent_pvt_sort_cmp, agent_pvt_cmp);
2537 return AST_MODULE_LOAD_FAILURE;
2539 if (load_config()) {
2540 ast_log(LOG_ERROR, "Unable to load config. Not loading module.\n");
2541 ao2_ref(agents, -1);
2543 return AST_MODULE_LOAD_DECLINE;
2546 /* Init agent holding bridge v_table. */
2547 bridge_init_agent_hold();
2549 /* Setup to provide Agent:agent-id device state. */
2550 res |= ast_devstate_prov_add("Agent", agent_pvt_devstate_get);
2553 res |= ast_cli_register_multiple(cli_agents, ARRAY_LEN(cli_agents));
2555 /* Manager commands */
2556 res |= ast_manager_register_xml("Agents", EVENT_FLAG_AGENT, action_agents);
2557 res |= ast_manager_register_xml("AgentLogoff", EVENT_FLAG_AGENT, action_agent_logoff);
2559 /* Dialplan Functions */
2560 res |= ast_custom_function_register(&agent_function);
2562 /* Dialplan applications */
2563 res |= ast_register_application_xml(app_agent_login, agent_login_exec);
2564 res |= ast_register_application_xml(app_agent_request, agent_request_exec);
2568 return AST_MODULE_LOAD_FAILURE;
2570 return AST_MODULE_LOAD_SUCCESS;
2573 static int reload(void)
2575 if (aco_process_config(&cfg_info, 1) == ACO_PROCESS_ERROR) {
2576 /* Just keep the config we already have in place. */
2582 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Call center agent pool applications",
2583 .load = load_module,
2584 .unload = unload_module,
2586 .load_pri = AST_MODPRI_DEVSTATE_PROVIDER,