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/bridging.h"
44 #include "asterisk/bridging_internal.h"
45 #include "asterisk/bridging_basic.h"
46 #include "asterisk/config_options.h"
47 #include "asterisk/features_config.h"
48 #include "asterisk/astobj2.h"
49 #include "asterisk/stringfields.h"
50 #include "asterisk/stasis_channels.h"
53 <application name="AgentLogin" language="en_US">
58 <parameter name="AgentId" required="true" />
59 <parameter name="options">
62 <para>silent login - do not announce the login ok segment after
63 agent logged on.</para>
69 <para>Login an agent to the system. Any agent authentication is assumed to
70 already be done by dialplan. While logged in, the agent can receive calls
71 and will hear the sound file specified by the config option custom_beep
72 when a new call comes in for the agent. Login failures will continue in
73 the dialplan with <variable>AGENT_STATUS</variable> set.</para>
74 <para>Before logging in, you can setup on the real agent channel the
75 CHANNEL(dtmf-features) an agent will have when talking to a caller
76 and you can setup on the channel running this application the
77 CONNECTEDLINE() information the agent will see while waiting for a
79 <para><variable>AGENT_STATUS</variable> enumeration values:</para>
81 <enum name = "INVALID"><para>The specified agent is invalid.</para></enum>
82 <enum name = "ALREADY_LOGGED_IN"><para>The agent is already logged in.</para></enum>
84 <note><para>The Agents:<replaceable>AgentId</replaceable> device state is
85 available to monitor the status of the agent.</para></note>
88 <ref type="application">Authenticate</ref>
89 <ref type="application">Queue</ref>
90 <ref type="application">AddQueueMember</ref>
91 <ref type="application">RemoveQueueMember</ref>
92 <ref type="application">PauseQueueMember</ref>
93 <ref type="application">UnpauseQueueMember</ref>
94 <ref type="function">AGENT</ref>
95 <ref type="function">CHANNEL(dtmf-features)</ref>
96 <ref type="function">CONNECTEDLINE()</ref>
97 <ref type="filename">agents.conf</ref>
98 <ref type="filename">queues.conf</ref>
101 <application name="AgentRequest" language="en_US">
103 Request an agent to connect with the channel.
106 <parameter name="AgentId" required="true" />
109 <para>Request an agent to connect with the channel. Failure to find and
110 alert an agent will continue in the dialplan with <variable>AGENT_STATUS</variable> set.</para>
111 <para><variable>AGENT_STATUS</variable> enumeration values:</para>
113 <enum name = "INVALID"><para>The specified agent is invalid.</para></enum>
114 <enum name = "NOT_LOGGED_IN"><para>The agent is not available.</para></enum>
115 <enum name = "BUSY"><para>The agent is on another call.</para></enum>
116 <enum name = "ERROR"><para>Alerting the agent failed.</para></enum>
120 <ref type="application">AgentLogin</ref>
123 <function name="AGENT" language="en_US">
125 Gets information about an Agent
128 <parameter name="AgentId" required="true" />
129 <parameter name="item">
130 <para>The valid items to retrieve are:</para>
133 <para>(default) The status of the agent (LOGGEDIN | LOGGEDOUT)</para>
135 <enum name="password">
136 <para>Deprecated. The dialplan handles any agent authentication.</para>
139 <para>The name of the agent</para>
141 <enum name="mohclass">
142 <para>MusicOnHold class</para>
144 <enum name="channel">
145 <para>The name of the active channel for the Agent (AgentLogin)</para>
147 <enum name="fullchannel">
148 <para>The untruncated name of the active channel for the Agent (AgentLogin)</para>
153 <description></description>
155 <manager name="Agents" language="en_US">
157 Lists agents and their status.
160 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
163 <para>Will list info about all defined agents.</para>
166 <ref type="managerEvent">Agents</ref>
167 <ref type="managerEvent">AgentsComplete</ref>
170 <managerEvent language="en_US" name="Agents">
171 <managerEventInstance class="EVENT_FLAG_AGENT">
173 Response event in a series to the Agents AMI action containing
174 information about a defined agent.
177 <parameter name="Agent">
178 <para>Agent ID of the agent.</para>
180 <parameter name="Name">
181 <para>User friendly name of the agent.</para>
183 <parameter name="Status">
184 <para>Current status of the agent.</para>
185 <para>The valid values are:</para>
187 <enum name="AGENT_LOGGEDOFF" />
188 <enum name="AGENT_IDLE" />
189 <enum name="AGENT_ONCALL" />
192 <parameter name="TalkingToChan">
193 <para><variable>BRIDGEPEER</variable> value on agent channel.</para>
194 <para>Present if Status value is <literal>AGENT_ONCALL</literal>.</para>
196 <parameter name="CallStarted">
197 <para>Epoche time when the agent started talking with the caller.</para>
198 <para>Present if Status value is <literal>AGENT_ONCALL</literal>.</para>
200 <parameter name="LoggedInTime">
201 <para>Epoche time when the agent logged in.</para>
202 <para>Present if Status value is <literal>AGENT_IDLE</literal> or <literal>AGENT_ONCALL</literal>.</para>
204 <parameter name="Channel">
205 <xi:include xpointer="xpointer(/docs/managerEvent[@name='Newchannel']/managerEventInstance/syntax/parameter[@name='Channel']/para)" />
206 <para>Present if Status value is <literal>AGENT_IDLE</literal> or <literal>AGENT_ONCALL</literal>.</para>
208 <parameter name="ChannelState">
209 <xi:include xpointer="xpointer(/docs/managerEvent[@name='Newchannel']/managerEventInstance/syntax/parameter[@name='ChannelState']/para)" />
210 <para>Present if Status value is <literal>AGENT_IDLE</literal> or <literal>AGENT_ONCALL</literal>.</para>
212 <parameter name="ChannelStateDesc">
213 <xi:include xpointer="xpointer(/docs/managerEvent[@name='Newchannel']/managerEventInstance/syntax/parameter[@name='ChannelStateDesc']/para)" />
214 <xi:include xpointer="xpointer(/docs/managerEvent[@name='Newchannel']/managerEventInstance/syntax/parameter[@name='ChannelStateDesc']/enumlist)" />
215 <para>Present if Status value is <literal>AGENT_IDLE</literal> or <literal>AGENT_ONCALL</literal>.</para>
217 <parameter name="CallerIDNum">
218 <para>Present if Status value is <literal>AGENT_IDLE</literal> or <literal>AGENT_ONCALL</literal>.</para>
220 <parameter name="CallerIDName">
221 <para>Present if Status value is <literal>AGENT_IDLE</literal> or <literal>AGENT_ONCALL</literal>.</para>
223 <parameter name="ConnectedLineNum">
224 <para>Present if Status value is <literal>AGENT_IDLE</literal> or <literal>AGENT_ONCALL</literal>.</para>
226 <parameter name="ConnectedLineName">
227 <para>Present if Status value is <literal>AGENT_IDLE</literal> or <literal>AGENT_ONCALL</literal>.</para>
229 <parameter name="AccountCode">
230 <para>Present if Status value is <literal>AGENT_IDLE</literal> or <literal>AGENT_ONCALL</literal>.</para>
232 <parameter name="Context">
233 <para>Present if Status value is <literal>AGENT_IDLE</literal> or <literal>AGENT_ONCALL</literal>.</para>
235 <parameter name="Exten">
236 <para>Present if Status value is <literal>AGENT_IDLE</literal> or <literal>AGENT_ONCALL</literal>.</para>
238 <parameter name="Priority">
239 <para>Present if Status value is <literal>AGENT_IDLE</literal> or <literal>AGENT_ONCALL</literal>.</para>
241 <parameter name="Uniqueid">
242 <xi:include xpointer="xpointer(/docs/managerEvent[@name='Newchannel']/managerEventInstance/syntax/parameter[@name='Uniqueid']/para)" />
243 <para>Present if Status value is <literal>AGENT_IDLE</literal> or <literal>AGENT_ONCALL</literal>.</para>
245 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
248 <ref type="manager">Agents</ref>
250 </managerEventInstance>
252 <managerEvent language="en_US" name="AgentsComplete">
253 <managerEventInstance class="EVENT_FLAG_AGENT">
255 Final response event in a series of events to the Agents AMI action.
258 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
261 <ref type="manager">Agents</ref>
263 </managerEventInstance>
265 <manager name="AgentLogoff" language="en_US">
267 Sets an agent as no longer logged in.
270 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
271 <parameter name="Agent" required="true">
272 <para>Agent ID of the agent to log off.</para>
274 <parameter name="Soft">
275 <para>Set to <literal>true</literal> to not hangup existing calls.</para>
279 <para>Sets an agent as no longer logged in.</para>
282 <configInfo name="app_agent_pool" language="en_US">
283 <synopsis>Agent pool applications</synopsis>
285 <note><para>Option changes take effect on agent login or after an agent
286 disconnects from a call.</para></note>
288 <configFile name="agents.conf">
289 <configObject name="global">
290 <synopsis>Unused, but reserved.</synopsis>
292 <configObject name="agent-id">
293 <synopsis>Configure an agent for the pool.</synopsis>
295 <xi:include xpointer="xpointer(/docs/configInfo[@name='app_agent_pool']/description/note)" />
297 <configOption name="ackcall">
298 <synopsis>Enable to require the agent to acknowledge a call.</synopsis>
300 <para>Enable to require the agent to give a DTMF acknowledgement
301 when the agent receives a call.</para>
302 <note><para>The option is overridden by <variable>AGENTACKCALL</variable> on agent login.</para></note>
303 <xi:include xpointer="xpointer(/docs/configInfo[@name='app_agent_pool']/description/note)" />
306 <configOption name="acceptdtmf">
307 <synopsis>DTMF key sequence the agent uses to acknowledge a call.</synopsis>
309 <note><para>The option is overridden by <variable>AGENTACCEPTDTMF</variable> on agent login.</para></note>
310 <note><para>The option is ignored unless the ackcall option is enabled.</para></note>
311 <xi:include xpointer="xpointer(/docs/configInfo[@name='app_agent_pool']/description/note)" />
314 <configOption name="autologoff">
315 <synopsis>Time the agent has to acknowledge a call before being logged off.</synopsis>
317 <para>Set how many seconds a call for the agent has to wait for the
318 agent to acknowledge the call before the agent is automatically
319 logged off. If set to zero then the call will wait forever for
320 the agent to acknowledge.</para>
321 <note><para>The option is overridden by <variable>AGENTAUTOLOGOFF</variable> on agent login.</para></note>
322 <note><para>The option is ignored unless the ackcall option is enabled.</para></note>
323 <xi:include xpointer="xpointer(/docs/configInfo[@name='app_agent_pool']/description/note)" />
326 <configOption name="wrapuptime">
327 <synopsis>Minimum time the agent has between calls.</synopsis>
329 <para>Set the minimum amount of time in milliseconds after
330 disconnecting a call before the agent can receive a new call.</para>
331 <note><para>The option is overridden by <variable>AGENTWRAPUPTIME</variable> on agent login.</para></note>
332 <xi:include xpointer="xpointer(/docs/configInfo[@name='app_agent_pool']/description/note)" />
335 <configOption name="musiconhold">
336 <synopsis>Music on hold class the agent listens to between calls.</synopsis>
338 <xi:include xpointer="xpointer(/docs/configInfo[@name='app_agent_pool']/description/note)" />
341 <configOption name="recordagentcalls">
342 <synopsis>Enable to automatically record calls the agent takes.</synopsis>
344 <para>Enable recording calls the agent takes automatically by
345 invoking the automixmon DTMF feature when the agent connects
346 to a caller. See <filename>features.conf.sample</filename> for information about
347 the automixmon feature.</para>
348 <xi:include xpointer="xpointer(/docs/configInfo[@name='app_agent_pool']/description/note)" />
351 <configOption name="custom_beep">
352 <synopsis>Sound file played to alert the agent when a call is present.</synopsis>
354 <xi:include xpointer="xpointer(/docs/configInfo[@name='app_agent_pool']/description/note)" />
357 <configOption name="fullname">
358 <synopsis>A friendly name for the agent used in log messages.</synopsis>
360 <xi:include xpointer="xpointer(/docs/configInfo[@name='app_agent_pool']/description/note)" />
368 /* ------------------------------------------------------------------- */
370 #define AST_MAX_BUF 256
372 /*! Maximum wait time (in ms) for the custom_beep file to play announcing the caller. */
373 #define CALLER_SAFETY_TIMEOUT_TIME (2 * 60 * 1000)
375 /*! Number of seconds to wait for local channel optimizations to complete. */
376 #define LOGIN_WAIT_TIMEOUT_TIME 5
378 static const char app_agent_login[] = "AgentLogin";
379 static const char app_agent_request[] = "AgentRequest";
381 /*! Agent config parameters. */
383 AST_DECLARE_STRING_FIELDS(
384 /*! Identification of the agent. (agents config container key) */
385 AST_STRING_FIELD(username);
386 /*! Name of agent for logging and querying purposes */
387 AST_STRING_FIELD(full_name);
390 * \brief DTMF string for an agent to accept a call.
392 * \note The channel variable AGENTACCEPTDTMF overrides on login.
394 AST_STRING_FIELD(dtmf_accept);
395 /*! Beep sound file to use. Alert the agent a call is waiting. */
396 AST_STRING_FIELD(beep_sound);
397 /*! MOH class to use while agent waiting for call. */
398 AST_STRING_FIELD(moh);
401 * \brief Number of seconds for agent to ack a call before being logged off.
403 * \note The channel variable AGENTAUTOLOGOFF overrides on login.
404 * \note If zero then timer is disabled.
406 unsigned int auto_logoff;
408 * \brief Time after a call in ms before the agent can get a new call.
410 * \note The channel variable AGENTWRAPUPTIME overrides on login.
412 unsigned int wrapup_time;
414 * \brief TRUE if agent needs to ack a call to accept it.
416 * \note The channel variable AGENTACKCALL overrides on login.
419 /*! TRUE if agent calls are automatically recorded. */
420 int record_agent_calls;
425 * \brief Agent config ao2 container sort function.
428 * \param obj_left pointer to the (user-defined part) of an object.
429 * \param obj_right pointer to the (user-defined part) of an object.
430 * \param flags flags from ao2_callback()
431 * OBJ_POINTER - if set, 'obj_right', is an object.
432 * OBJ_KEY - if set, 'obj_right', is a search key item that is not an object.
433 * OBJ_PARTIAL_KEY - if set, 'obj_right', is a partial search key item that is not an object.
435 * \retval <0 if obj_left < obj_right
436 * \retval =0 if obj_left == obj_right
437 * \retval >0 if obj_left > obj_right
439 static int agent_cfg_sort_cmp(const void *obj_left, const void *obj_right, int flags)
441 const struct agent_cfg *cfg_left = obj_left;
442 const struct agent_cfg *cfg_right = obj_right;
443 const char *right_key = obj_right;
446 switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
449 right_key = cfg_right->username;
452 cmp = strcmp(cfg_left->username, right_key);
454 case OBJ_PARTIAL_KEY:
455 cmp = strncmp(cfg_left->username, right_key, strlen(right_key));
461 static void agent_cfg_destructor(void *vdoomed)
463 struct agent_cfg *doomed = vdoomed;
465 ast_string_field_free_memory(doomed);
468 static void *agent_cfg_alloc(const char *name)
470 struct agent_cfg *cfg;
472 cfg = ao2_alloc_options(sizeof(*cfg), agent_cfg_destructor,
473 AO2_ALLOC_OPT_LOCK_NOLOCK);
474 if (!cfg || ast_string_field_init(cfg, 64)) {
477 ast_string_field_set(cfg, username, name);
481 static void *agent_cfg_find(struct ao2_container *agents, const char *username)
483 return ao2_find(agents, username, OBJ_KEY);
486 /*! Agents configuration */
488 /*! Master configured agents container. */
489 struct ao2_container *agents;
492 static struct aco_type agent_type = {
495 .category_match = ACO_BLACKLIST,
496 .category = "^(general|agents)$",
497 .item_alloc = agent_cfg_alloc,
498 .item_find = agent_cfg_find,
499 .item_offset = offsetof(struct agents_cfg, agents),
502 static struct aco_type *agent_types[] = ACO_TYPES(&agent_type);
504 /* The general category is reserved, but unused */
505 static struct aco_type general_type = {
508 .category_match = ACO_WHITELIST,
509 .category = "^general$",
512 static struct aco_file agents_conf = {
513 .filename = "agents.conf",
514 .types = ACO_TYPES(&general_type, &agent_type),
517 static AO2_GLOBAL_OBJ_STATIC(cfg_handle);
519 static void agents_cfg_destructor(void *vdoomed)
521 struct agents_cfg *doomed = vdoomed;
523 ao2_cleanup(doomed->agents);
524 doomed->agents = NULL;
529 * \brief Create struct agents_cfg object.
532 * \note A lock is not needed for the object or any secondary
533 * created cfg objects. These objects are immutable after the
534 * config is loaded and applied.
536 * \retval New struct agents_cfg object.
537 * \retval NULL on error.
539 static void *agents_cfg_alloc(void)
541 struct agents_cfg *cfg;
543 cfg = ao2_alloc_options(sizeof(*cfg), agents_cfg_destructor,
544 AO2_ALLOC_OPT_LOCK_NOLOCK);
548 cfg->agents = ao2_container_alloc_rbtree(AO2_ALLOC_OPT_LOCK_NOLOCK,
549 AO2_CONTAINER_ALLOC_OPT_DUPS_REJECT, agent_cfg_sort_cmp, NULL);
557 static void agents_post_apply_config(void);
559 CONFIG_INFO_STANDARD(cfg_info, cfg_handle, agents_cfg_alloc,
560 .files = ACO_FILES(&agents_conf),
561 .post_apply_config = agents_post_apply_config,
564 static void destroy_config(void)
566 ao2_global_obj_release(cfg_handle);
567 aco_info_destroy(&cfg_info);
570 static int load_config(void)
572 if (aco_info_init(&cfg_info)) {
577 aco_option_register(&cfg_info, "ackcall", ACO_EXACT, agent_types, "no", OPT_BOOL_T, 1, FLDSET(struct agent_cfg, ack_call));
578 aco_option_register(&cfg_info, "acceptdtmf", ACO_EXACT, agent_types, "#", OPT_STRINGFIELD_T, 1, STRFLDSET(struct agent_cfg, dtmf_accept));
579 aco_option_register(&cfg_info, "autologoff", ACO_EXACT, agent_types, "0", OPT_UINT_T, 0, FLDSET(struct agent_cfg, auto_logoff));
580 aco_option_register(&cfg_info, "wrapuptime", ACO_EXACT, agent_types, "0", OPT_UINT_T, 0, FLDSET(struct agent_cfg, wrapup_time));
581 aco_option_register(&cfg_info, "musiconhold", ACO_EXACT, agent_types, "default", OPT_STRINGFIELD_T, 0, STRFLDSET(struct agent_cfg, moh));
582 aco_option_register(&cfg_info, "recordagentcalls", ACO_EXACT, agent_types, "no", OPT_BOOL_T, 1, FLDSET(struct agent_cfg, record_agent_calls));
583 aco_option_register(&cfg_info, "custom_beep", ACO_EXACT, agent_types, "beep", OPT_STRINGFIELD_T, 1, STRFLDSET(struct agent_cfg, beep_sound));
584 aco_option_register(&cfg_info, "fullname", ACO_EXACT, agent_types, NULL, OPT_STRINGFIELD_T, 0, STRFLDSET(struct agent_cfg, full_name));
586 if (aco_process_config(&cfg_info, 0) == ACO_PROCESS_ERROR) {
598 /*! The agent is defined but an agent is not present. */
599 AGENT_STATE_LOGGED_OUT,
600 /*! Forced initial login wait to allow any local channel optimizations to happen. */
601 AGENT_STATE_PROBATION_WAIT,
602 /*! The agent is ready for a call. */
603 AGENT_STATE_READY_FOR_CALL,
604 /*! The agent has a call waiting to connect. */
605 AGENT_STATE_CALL_PRESENT,
606 /*! The agent needs to ack the call. */
607 AGENT_STATE_CALL_WAIT_ACK,
608 /*! The agent is connected with a call. */
610 /*! The agent is resting between calls. */
611 AGENT_STATE_CALL_WRAPUP,
612 /*! The agent is being kicked out. */
613 AGENT_STATE_LOGGING_OUT,
616 /*! Agent config option override flags. */
617 enum agent_override_flags {
618 AGENT_FLAG_ACK_CALL = (1 << 0),
619 AGENT_FLAG_DTMF_ACCEPT = (1 << 1),
620 AGENT_FLAG_AUTO_LOGOFF = (1 << 2),
621 AGENT_FLAG_WRAPUP_TIME = (1 << 3),
624 /*! \brief Structure representing an agent. */
626 AST_DECLARE_STRING_FIELDS(
627 /*! Identification of the agent. (agents container key) */
628 AST_STRING_FIELD(username);
629 /*! Login override DTMF string for an agent to accept a call. */
630 AST_STRING_FIELD(override_dtmf_accept);
632 /*! Connected line information to send when reentering the holding bridge. */
633 struct ast_party_connected_line waiting_colp;
634 /*! Flags show if settings were overridden by channel vars. */
636 /*! Login override number of seconds for agent to ack a call before being logged off. */
637 unsigned int override_auto_logoff;
638 /*! Login override time after a call in ms before the agent can get a new call. */
639 unsigned int override_wrapup_time;
640 /*! Login override if agent needs to ack a call to accept it. */
641 unsigned int override_ack_call:1;
643 /*! TRUE if the agent is requested to logoff when the current call ends. */
644 unsigned int deferred_logoff:1;
646 /*! Mark and sweep config update to determine if an agent is dead. */
647 unsigned int the_mark:1;
649 * \brief TRUE if the agent is no longer configured and is being destroyed.
651 * \note Agents cannot log in if they are dead.
655 /*! Agent control state variable. */
656 enum agent_state state;
657 /*! Custom device state of agent. */
658 enum ast_device_state devstate;
660 /*! When agent first logged in */
662 /*! When agent login probation started. */
663 time_t probation_start;
664 /*! When call started */
666 /*! When ack timer started */
667 struct timeval ack_time;
668 /*! When last disconnected */
669 struct timeval last_disconnect;
671 /*! Caller is waiting in this bridge for agent to join. (Holds ref) */
672 struct ast_bridge *caller_bridge;
673 /*! Agent is logged in with this channel. (Holds ref) (NULL if not logged in.) */
674 struct ast_channel *logged;
675 /*! Active config values from config file. (Holds ref) */
676 struct agent_cfg *cfg;
679 /*! Container of defined agents. */
680 static struct ao2_container *agents;
683 * \brief Lock the agent.
685 * \param agent Agent to lock
689 #define agent_lock(agent) _agent_lock(agent, __FILE__, __PRETTY_FUNCTION__, __LINE__, #agent)
690 static inline void _agent_lock(struct agent_pvt *agent, const char *file, const char *function, int line, const char *var)
692 __ao2_lock(agent, AO2_LOCK_REQ_MUTEX, file, function, line, var);
696 * \brief Unlock the agent.
698 * \param agent Agent to unlock
702 #define agent_unlock(agent) _agent_unlock(agent, __FILE__, __PRETTY_FUNCTION__, __LINE__, #agent)
703 static inline void _agent_unlock(struct agent_pvt *agent, const char *file, const char *function, int line, const char *var)
705 __ao2_unlock(agent, file, function, line, var);
710 * \brief Obtain the agent logged in channel lock if it exists.
713 * \param agent Pointer to the LOCKED agent_pvt.
715 * \note Assumes the agent lock is already obtained.
717 * \note Defined locking order is channel lock then agent lock.
721 static struct ast_channel *agent_lock_logged(struct agent_pvt *agent)
723 struct ast_channel *logged;
726 if (!agent->logged) { /* No owner. Nothing to do. */
730 /* If we don't ref the logged, it could be killed when we unlock the agent. */
731 logged = ast_channel_ref(agent->logged);
733 /* Locking logged requires us to lock channel, then agent. */
735 ast_channel_lock(logged);
738 /* Check if logged changed during agent unlock period */
739 if (logged != agent->logged) {
740 /* Channel changed. Unref and do another pass. */
741 ast_channel_unlock(logged);
742 ast_channel_unref(logged);
744 /* Channel stayed the same. Return it. */
752 * \brief Get the Agent:agent_id device state.
755 * \param agent_id Username of the agent.
758 * Search the agents container for the agent and return the
761 * \return Device state of the agent.
763 static enum ast_device_state agent_pvt_devstate_get(const char *agent_id)
765 RAII_VAR(struct agent_pvt *, agent, ao2_find(agents, agent_id, OBJ_KEY), ao2_cleanup);
768 return agent->devstate;
770 return AST_DEVICE_INVALID;
775 * \brief Request an agent device state be updated.
778 * \param agent_id Which agent needs the device state updated.
782 static void agent_devstate_changed(const char *agent_id)
784 ast_devstate_changed(AST_DEVICE_UNKNOWN, AST_DEVSTATE_CACHABLE, "Agent:%s", agent_id);
787 static void agent_pvt_destructor(void *vdoomed)
789 struct agent_pvt *doomed = vdoomed;
791 /* Make sure device state reflects agent destruction. */
792 if (!ast_strlen_zero(doomed->username)) {
793 ast_debug(1, "Agent %s: Destroyed.\n", doomed->username);
794 agent_devstate_changed(doomed->username);
797 ast_party_connected_line_free(&doomed->waiting_colp);
798 if (doomed->caller_bridge) {
799 ast_bridge_destroy(doomed->caller_bridge);
800 doomed->caller_bridge = NULL;
802 if (doomed->logged) {
803 doomed->logged = ast_channel_unref(doomed->logged);
805 ao2_cleanup(doomed->cfg);
807 ast_string_field_free_memory(doomed);
810 static struct agent_pvt *agent_pvt_new(struct agent_cfg *cfg)
812 struct agent_pvt *agent;
814 agent = ao2_alloc(sizeof(*agent), agent_pvt_destructor);
818 if (ast_string_field_init(agent, 32)) {
822 ast_string_field_set(agent, username, cfg->username);
823 ast_party_connected_line_init(&agent->waiting_colp);
826 agent->devstate = AST_DEVICE_UNAVAILABLE;
832 * \brief Agents ao2 container sort function.
835 * \param obj_left pointer to the (user-defined part) of an object.
836 * \param obj_right pointer to the (user-defined part) of an object.
837 * \param flags flags from ao2_callback()
838 * OBJ_POINTER - if set, 'obj_right', is an object.
839 * OBJ_KEY - if set, 'obj_right', is a search key item that is not an object.
840 * OBJ_PARTIAL_KEY - if set, 'obj_right', is a partial search key item that is not an object.
842 * \retval <0 if obj_left < obj_right
843 * \retval =0 if obj_left == obj_right
844 * \retval >0 if obj_left > obj_right
846 static int agent_pvt_sort_cmp(const void *obj_left, const void *obj_right, int flags)
848 const struct agent_pvt *agent_left = obj_left;
849 const struct agent_pvt *agent_right = obj_right;
850 const char *right_key = obj_right;
853 switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
856 right_key = agent_right->username;
859 cmp = strcmp(agent_left->username, right_key);
861 case OBJ_PARTIAL_KEY:
862 cmp = strncmp(agent_left->username, right_key, strlen(right_key));
870 * \brief ao2_find() callback function.
874 * found = ao2_find(agents, agent, OBJ_POINTER);
875 * found = ao2_find(agents, "agent-id", OBJ_KEY);
876 * found = ao2_find(agents, agent->logged, 0);
878 static int agent_pvt_cmp(void *obj, void *arg, int flags)
880 const struct agent_pvt *agent = obj;
883 switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
886 case OBJ_PARTIAL_KEY:
890 if (agent->logged == arg) {
900 static int agent_mark(void *obj, void *arg, int flags)
902 struct agent_pvt *agent = obj;
910 static void agents_mark(void)
912 ao2_callback(agents, 0, agent_mark, NULL);
915 static int agent_sweep(void *obj, void *arg, int flags)
917 struct agent_pvt *agent = obj;
921 if (agent->the_mark) {
924 /* Unlink dead agents immediately. */
931 static void agents_sweep(void)
933 struct ao2_iterator *iter;
934 struct agent_pvt *agent;
935 struct ast_channel *logged;
937 iter = ao2_callback(agents, OBJ_MULTIPLE | OBJ_UNLINK, agent_sweep, NULL);
941 for (; (agent = ao2_iterator_next(iter)); ao2_ref(agent, -1)) {
944 logged = ast_channel_ref(agent->logged);
953 "Forced logoff of agent %s(%s). Agent no longer configured.\n",
954 agent->username, ast_channel_name(logged));
955 ast_softhangup(logged, AST_SOFTHANGUP_EXPLICIT);
956 ast_channel_unref(logged);
958 ao2_iterator_destroy(iter);
961 static void agents_post_apply_config(void)
963 struct ao2_iterator iter;
964 struct agent_cfg *cfg;
965 RAII_VAR(struct agents_cfg *, cfgs, ao2_global_obj_ref(cfg_handle), ao2_cleanup);
967 ast_assert(cfgs != NULL);
970 iter = ao2_iterator_init(cfgs->agents, 0);
971 for (; (cfg = ao2_iterator_next(&iter)); ao2_ref(cfg, -1)) {
972 RAII_VAR(struct agent_pvt *, agent, ao2_find(agents, cfg->username, OBJ_KEY), ao2_cleanup);
977 if (!agent->logged) {
978 struct agent_cfg *cfg_old;
980 /* Replace the config of agents not logged in. */
981 cfg_old = agent->cfg;
984 ao2_cleanup(cfg_old);
989 agent = agent_pvt_new(cfg);
993 ao2_link(agents, agent);
994 ast_debug(1, "Agent %s: Created.\n", agent->username);
995 agent_devstate_changed(agent->username);
997 ao2_iterator_destroy(&iter);
1001 static int agent_logoff_request(const char *agent_id, int soft)
1003 struct ast_channel *logged;
1004 RAII_VAR(struct agent_pvt *, agent, ao2_find(agents, agent_id, OBJ_KEY), ao2_cleanup);
1011 logged = agent_lock_logged(agent);
1014 agent->deferred_logoff = 1;
1016 ast_softhangup(logged, AST_SOFTHANGUP_EXPLICIT);
1018 ast_channel_unlock(logged);
1019 ast_channel_unref(logged);
1021 agent_unlock(agent);
1025 /*! Agent holding bridge instance holder. */
1026 static AO2_GLOBAL_OBJ_STATIC(agent_holding);
1028 /*! Agent holding bridge deferred creation lock. */
1029 AST_MUTEX_DEFINE_STATIC(agent_holding_lock);
1033 * \brief Connect the agent with the waiting caller.
1036 * \param bridge_channel Agent channel connecting to the caller.
1037 * \param agent Which agent is connecting to the caller.
1039 * \note The agent is locked on entry and not locked on exit.
1043 static void agent_connect_caller(struct ast_bridge_channel *bridge_channel, struct agent_pvt *agent)
1045 struct ast_bridge *caller_bridge;
1046 int record_agent_calls;
1049 record_agent_calls = agent->cfg->record_agent_calls;
1050 caller_bridge = agent->caller_bridge;
1051 agent->caller_bridge = NULL;
1052 agent->state = AGENT_STATE_ON_CALL;
1053 time(&agent->call_start);
1054 agent_unlock(agent);
1056 if (!caller_bridge) {
1058 ast_bridge_channel_leave_bridge(bridge_channel);
1061 res = ast_bridge_move(caller_bridge, bridge_channel->bridge, bridge_channel->chan,
1065 ast_bridge_destroy(caller_bridge);
1066 ast_bridge_channel_leave_bridge(bridge_channel);
1069 ast_bridge_channel_write_control_data(bridge_channel, AST_CONTROL_ANSWER, NULL, 0);
1071 if (record_agent_calls) {
1072 struct ast_bridge_features_automixmonitor options = {
1073 .start_stop = AUTO_MONITOR_START,
1077 * The agent is in the new bridge so we can invoke the
1078 * mixmonitor hook to only start recording.
1080 ast_bridge_features_do(AST_BRIDGE_BUILTIN_AUTOMIXMON, caller_bridge,
1081 bridge_channel, &options);
1085 static int bridge_agent_hold_ack(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
1087 struct agent_pvt *agent = hook_pvt;
1090 switch (agent->state) {
1091 case AGENT_STATE_CALL_WAIT_ACK:
1092 /* Connect to caller now. */
1093 ast_debug(1, "Agent %s: Acked call.\n", agent->username);
1094 agent_connect_caller(bridge_channel, agent);/* Will unlock agent. */
1099 agent_unlock(agent);
1103 static int bridge_agent_hold_heartbeat(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
1105 struct agent_pvt *agent = hook_pvt;
1106 int probation_timedout = 0;
1107 int ack_timedout = 0;
1108 int wrapup_timedout = 0;
1109 int deferred_logoff;
1110 unsigned int wrapup_time;
1111 unsigned int auto_logoff;
1114 deferred_logoff = agent->deferred_logoff;
1115 if (deferred_logoff) {
1116 agent->state = AGENT_STATE_LOGGING_OUT;
1119 switch (agent->state) {
1120 case AGENT_STATE_PROBATION_WAIT:
1121 probation_timedout =
1122 LOGIN_WAIT_TIMEOUT_TIME <= (time(NULL) - agent->probation_start);
1123 if (probation_timedout) {
1124 /* Now ready for a caller. */
1125 agent->state = AGENT_STATE_READY_FOR_CALL;
1126 agent->devstate = AST_DEVICE_NOT_INUSE;
1129 case AGENT_STATE_CALL_WAIT_ACK:
1130 /* Check ack call time. */
1131 auto_logoff = agent->cfg->auto_logoff;
1132 if (ast_test_flag(agent, AGENT_FLAG_AUTO_LOGOFF)) {
1133 auto_logoff = agent->override_auto_logoff;
1136 auto_logoff *= 1000;
1137 ack_timedout = ast_tvdiff_ms(ast_tvnow(), agent->ack_time) > auto_logoff;
1139 agent->state = AGENT_STATE_LOGGING_OUT;
1143 case AGENT_STATE_CALL_WRAPUP:
1144 /* Check wrapup time. */
1145 wrapup_time = agent->cfg->wrapup_time;
1146 if (ast_test_flag(agent, AGENT_FLAG_WRAPUP_TIME)) {
1147 wrapup_time = agent->override_wrapup_time;
1149 wrapup_timedout = ast_tvdiff_ms(ast_tvnow(), agent->last_disconnect) > wrapup_time;
1150 if (wrapup_timedout) {
1151 agent->state = AGENT_STATE_READY_FOR_CALL;
1152 agent->devstate = AST_DEVICE_NOT_INUSE;
1158 agent_unlock(agent);
1160 if (deferred_logoff) {
1161 ast_debug(1, "Agent %s: Deferred logoff.\n", agent->username);
1162 ast_bridge_channel_leave_bridge(bridge_channel);
1163 } else if (probation_timedout) {
1164 ast_debug(1, "Agent %s: Login complete.\n", agent->username);
1165 agent_devstate_changed(agent->username);
1166 } else if (ack_timedout) {
1167 ast_debug(1, "Agent %s: Ack call timeout.\n", agent->username);
1168 ast_bridge_channel_leave_bridge(bridge_channel);
1169 } else if (wrapup_timedout) {
1170 ast_debug(1, "Agent %s: Wrapup timeout. Ready for new call.\n", agent->username);
1171 agent_devstate_changed(agent->username);
1177 static void agent_after_bridge_cb(struct ast_channel *chan, void *data);
1178 static void agent_after_bridge_cb_failed(enum ast_after_bridge_cb_reason reason, void *data);
1182 * \brief ast_bridge agent_hold push method.
1185 * \param self Bridge to operate upon.
1186 * \param bridge_channel Bridge channel to push.
1187 * \param swap Bridge channel to swap places with if not NULL.
1189 * \note On entry, self is already locked.
1191 * \retval 0 on success
1192 * \retval -1 on failure
1194 static int bridge_agent_hold_push(struct ast_bridge *self, struct ast_bridge_channel *bridge_channel, struct ast_bridge_channel *swap)
1197 unsigned int wrapup_time;
1198 char dtmf[AST_FEATURE_MAX_LEN];
1199 struct ast_channel *chan;
1200 const char *moh_class;
1201 RAII_VAR(struct agent_pvt *, agent, NULL, ao2_cleanup);
1203 chan = bridge_channel->chan;
1205 agent = ao2_find(agents, swap ? swap->chan : chan, 0);
1207 /* Could not find the agent. */
1211 /* Setup agent entertainment */
1213 moh_class = ast_strdupa(agent->cfg->moh);
1214 agent_unlock(agent);
1215 res |= ast_channel_add_bridge_role(chan, "holding_participant");
1216 res |= ast_channel_set_bridge_role_option(chan, "holding_participant", "idle_mode", "musiconhold");
1217 res |= ast_channel_set_bridge_role_option(chan, "holding_participant", "moh_class", moh_class);
1219 /* Add DTMF acknowledge hook. */
1222 if (ast_test_flag(agent, AGENT_FLAG_ACK_CALL)
1223 ? agent->override_ack_call : agent->cfg->ack_call) {
1224 const char *dtmf_accept;
1226 dtmf_accept = ast_test_flag(agent, AGENT_FLAG_DTMF_ACCEPT)
1227 ? agent->override_dtmf_accept : agent->cfg->dtmf_accept;
1228 ast_copy_string(dtmf, dtmf_accept, sizeof(dtmf));
1230 agent_unlock(agent);
1231 if (!ast_strlen_zero(dtmf)) {
1233 if (ast_bridge_dtmf_hook(bridge_channel->features, dtmf, bridge_agent_hold_ack,
1234 agent, __ao2_cleanup, AST_BRIDGE_HOOK_REMOVE_ON_PULL)) {
1240 /* Add heartbeat interval hook. */
1242 if (ast_bridge_interval_hook(bridge_channel->features, 1000,
1243 bridge_agent_hold_heartbeat, agent, __ao2_cleanup, AST_BRIDGE_HOOK_REMOVE_ON_PULL)) {
1248 res |= ast_bridge_base_v_table.push(self, bridge_channel, swap);
1250 ast_channel_remove_bridge_role(chan, "holding_participant");
1255 res = ast_after_bridge_callback_set(chan, agent_after_bridge_cb,
1256 agent_after_bridge_cb_failed, chan);
1258 ast_channel_remove_bridge_role(chan, "holding_participant");
1263 ast_channel_unref(agent->logged);
1264 agent->logged = ast_channel_ref(chan);
1265 agent_unlock(agent);
1268 * Kick the channel out so it can come back in fully controlled.
1269 * Otherwise, the after bridge callback will linger and the
1270 * agent will have some slightly different behavior in corner
1273 ast_bridge_channel_leave_bridge(bridge_channel);
1278 switch (agent->state) {
1279 case AGENT_STATE_LOGGED_OUT:
1281 * \todo XXX the login probation time should be only if it is needed.
1283 * Need to determine if there are any local channels that can
1284 * optimize and wait until they actually do before leaving the
1285 * AGENT_STATE_PROBATION_WAIT state. For now, the blind
1286 * timer of LOGIN_WAIT_TIMEOUT_TIME will do.
1289 * Start the login probation timer.
1291 * We cannot handle an agent local channel optimization when the
1292 * agent is on a call. The optimization may kick the agent
1293 * channel we know about out of the call without our being able
1294 * to switch to the replacement channel. Get any agent local
1295 * channel optimization out of the way while the agent is in the
1298 time(&agent->probation_start);
1299 agent->state = AGENT_STATE_PROBATION_WAIT;
1300 agent_unlock(agent);
1302 case AGENT_STATE_PROBATION_WAIT:
1303 /* Restart the probation timer. */
1304 time(&agent->probation_start);
1305 agent_unlock(agent);
1307 case AGENT_STATE_READY_FOR_CALL:
1309 * Likely someone manually kicked us out of the holding bridge
1310 * and we came right back in.
1312 agent_unlock(agent);
1315 /* Unexpected agent state. */
1318 case AGENT_STATE_CALL_PRESENT:
1319 case AGENT_STATE_CALL_WAIT_ACK:
1320 agent->state = AGENT_STATE_READY_FOR_CALL;
1321 agent->devstate = AST_DEVICE_NOT_INUSE;
1322 agent_unlock(agent);
1323 ast_debug(1, "Agent %s: Call abort recovery complete.\n", agent->username);
1324 agent_devstate_changed(agent->username);
1326 case AGENT_STATE_ON_CALL:
1327 case AGENT_STATE_CALL_WRAPUP:
1328 wrapup_time = agent->cfg->wrapup_time;
1329 if (ast_test_flag(agent, AGENT_FLAG_WRAPUP_TIME)) {
1330 wrapup_time = agent->override_wrapup_time;
1333 agent->state = AGENT_STATE_CALL_WRAPUP;
1335 agent->state = AGENT_STATE_READY_FOR_CALL;
1336 agent->devstate = AST_DEVICE_NOT_INUSE;
1338 agent_unlock(agent);
1340 /* No wrapup time. */
1341 ast_debug(1, "Agent %s: Ready for new call.\n", agent->username);
1342 agent_devstate_changed(agent->username);
1352 * \brief ast_bridge agent_hold pull method.
1354 * \param self Bridge to operate upon.
1355 * \param bridge_channel Bridge channel to pull.
1358 * Remove any channel hooks controlled by the bridge. Release
1359 * any resources held by bridge_channel->bridge_pvt and release
1360 * bridge_channel->bridge_pvt.
1362 * \note On entry, self is already locked.
1366 static void bridge_agent_hold_pull(struct ast_bridge *self, struct ast_bridge_channel *bridge_channel)
1368 ast_channel_remove_bridge_role(bridge_channel->chan, "holding_participant");
1369 ast_bridge_base_v_table.pull(self, bridge_channel);
1373 * \brief The bridge is being dissolved.
1375 * \param self Bridge to operate upon.
1378 * The bridge is being dissolved. Remove any external
1379 * references to the bridge so it can be destroyed.
1381 * \note On entry, self must NOT be locked.
1385 static void bridge_agent_hold_dissolving(struct ast_bridge *self)
1387 ao2_global_obj_replace_unref(agent_holding, NULL);
1388 ast_bridge_base_v_table.dissolving(self);
1391 static struct ast_bridge_methods bridge_agent_hold_v_table;
1393 static struct ast_bridge *bridge_agent_hold_new(void)
1395 struct ast_bridge *bridge;
1397 bridge = bridge_alloc(sizeof(struct ast_bridge), &bridge_agent_hold_v_table);
1398 bridge = bridge_base_init(bridge, AST_BRIDGE_CAPABILITY_HOLDING,
1399 AST_BRIDGE_FLAG_MERGE_INHIBIT_TO | AST_BRIDGE_FLAG_MERGE_INHIBIT_FROM
1400 | AST_BRIDGE_FLAG_SWAP_INHIBIT_FROM | AST_BRIDGE_FLAG_TRANSFER_PROHIBITED);
1401 bridge = bridge_register(bridge);
1405 static void bridging_init_agent_hold(void)
1407 /* Setup bridge agent_hold subclass v_table. */
1408 bridge_agent_hold_v_table = ast_bridge_base_v_table;
1409 bridge_agent_hold_v_table.name = "agent_hold";
1410 bridge_agent_hold_v_table.dissolving = bridge_agent_hold_dissolving;
1411 bridge_agent_hold_v_table.push = bridge_agent_hold_push;
1412 bridge_agent_hold_v_table.pull = bridge_agent_hold_pull;
1415 static int bridge_agent_hold_deferred_create(void)
1417 RAII_VAR(struct ast_bridge *, holding, ao2_global_obj_ref(agent_holding), ao2_cleanup);
1420 ast_mutex_lock(&agent_holding_lock);
1421 holding = ao2_global_obj_ref(agent_holding);
1423 holding = bridge_agent_hold_new();
1424 ao2_global_obj_replace_unref(agent_holding, holding);
1426 ast_mutex_unlock(&agent_holding_lock);
1428 ast_log(LOG_ERROR, "Could not create agent holding bridge.\n");
1435 static void send_agent_login(struct ast_channel *chan, const char *agent)
1437 RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
1439 ast_assert(agent != NULL);
1441 blob = ast_json_pack("{s: s}",
1447 ast_channel_publish_blob(chan, ast_channel_agent_login_type(), blob);
1450 static void send_agent_logoff(struct ast_channel *chan, const char *agent, long logintime)
1452 RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
1454 ast_assert(agent != NULL);
1456 blob = ast_json_pack("{s: s, s: i}",
1458 "logintime", logintime);
1463 ast_channel_publish_blob(chan, ast_channel_agent_logoff_type(), blob);
1468 * \brief Logout the agent.
1471 * \param agent Which agent logging out.
1473 * \note On entry agent is already locked. On exit it is no longer locked.
1477 static void agent_logout(struct agent_pvt *agent)
1479 struct ast_channel *logged;
1480 struct ast_bridge *caller_bridge;
1481 long time_logged_in;
1483 time_logged_in = time(NULL) - agent->login_start;
1484 logged = agent->logged;
1485 agent->logged = NULL;
1486 caller_bridge = agent->caller_bridge;
1487 agent->caller_bridge = NULL;
1488 agent->state = AGENT_STATE_LOGGED_OUT;
1489 agent->devstate = AST_DEVICE_UNAVAILABLE;
1490 ast_clear_flag(agent, AST_FLAGS_ALL);
1491 agent_unlock(agent);
1492 agent_devstate_changed(agent->username);
1494 if (caller_bridge) {
1495 ast_bridge_destroy(caller_bridge);
1498 send_agent_logoff(logged, agent->username, time_logged_in);
1499 ast_verb(2, "Agent '%s' logged out. Logged in for %ld seconds.\n",
1500 agent->username, time_logged_in);
1501 ast_channel_unref(logged);
1506 * \brief Agent driver loop.
1509 * \param agent Which agent.
1510 * \param logged The logged in channel.
1514 static void agent_run(struct agent_pvt *agent, struct ast_channel *logged)
1516 struct ast_bridge_features features;
1518 if (ast_bridge_features_init(&features)) {
1519 goto agent_run_cleanup;
1522 struct agents_cfg *cfgs;
1523 struct agent_cfg *cfg_new;
1524 struct agent_cfg *cfg_old;
1525 struct ast_bridge *holding;
1526 struct ast_bridge *caller_bridge;
1528 holding = ao2_global_obj_ref(agent_holding);
1530 ast_debug(1, "Agent %s: Someone destroyed the agent holding bridge.\n",
1536 * When the agent channel leaves the bridging system we usually
1537 * want to put the agent back into the holding bridge for the
1540 ast_bridge_join(holding, logged, NULL, &features, NULL, 1);
1541 if (logged != agent->logged) {
1542 /* This channel is no longer the logged in agent. */
1547 /* The agent is no longer configured. */
1551 /* Update the agent's config before rejoining the holding bridge. */
1552 cfgs = ao2_global_obj_ref(cfg_handle);
1554 /* There is no agent configuration. All agents were destroyed. */
1557 cfg_new = ao2_find(cfgs->agents, agent->username, OBJ_KEY);
1560 /* The agent is no longer configured. */
1564 cfg_old = agent->cfg;
1565 agent->cfg = cfg_new;
1567 agent->last_disconnect = ast_tvnow();
1569 /* Clear out any caller bridge before rejoining the holding bridge. */
1570 caller_bridge = agent->caller_bridge;
1571 agent->caller_bridge = NULL;
1572 agent_unlock(agent);
1573 ao2_ref(cfg_old, -1);
1574 if (caller_bridge) {
1575 ast_bridge_destroy(caller_bridge);
1578 if (agent->state == AGENT_STATE_LOGGING_OUT
1579 || agent->deferred_logoff
1580 || ast_check_hangup_locked(logged)) {
1581 /* The agent was requested to logout or hungup. */
1586 * It is safe to access agent->waiting_colp without a lock. It
1587 * is only setup on agent login and not changed.
1589 ast_channel_update_connected_line(logged, &agent->waiting_colp, NULL);
1591 ast_bridge_features_cleanup(&features);
1595 if (logged != agent->logged) {
1597 * We are no longer the agent channel because of local channel
1600 agent_unlock(agent);
1601 ast_debug(1, "Agent %s: Channel %s is no longer the agent.\n",
1602 agent->username, ast_channel_name(logged));
1605 agent_logout(agent);
1608 static void agent_after_bridge_cb(struct ast_channel *chan, void *data)
1610 struct agent_pvt *agent;
1612 agent = ao2_find(agents, chan, 0);
1617 ast_debug(1, "Agent %s: New agent channel %s.\n",
1618 agent->username, ast_channel_name(chan));
1619 agent_run(agent, chan);
1623 static void agent_after_bridge_cb_failed(enum ast_after_bridge_cb_reason reason, void *data)
1625 struct ast_channel *chan = data;
1626 struct agent_pvt *agent;
1628 agent = ao2_find(agents, chan, 0);
1632 ast_log(LOG_WARNING, "Agent %s: Forced logout. Lost control of %s because: %s\n",
1633 agent->username, ast_channel_name(chan),
1634 ast_after_bridge_cb_reason_string(reason));
1636 agent_logout(agent);
1642 * \brief Get the lock on the agent bridge_channel and return it.
1645 * \param agent Whose bridge_channel to get.
1647 * \retval bridge_channel on success (Reffed and locked).
1648 * \retval NULL on error.
1650 static struct ast_bridge_channel *agent_bridge_channel_get_lock(struct agent_pvt *agent)
1652 struct ast_channel *logged;
1653 struct ast_bridge_channel *bc;
1657 logged = agent->logged;
1659 agent_unlock(agent);
1662 ast_channel_ref(logged);
1663 agent_unlock(agent);
1665 ast_channel_lock(logged);
1666 bc = ast_channel_get_bridge_channel(logged);
1667 ast_channel_unlock(logged);
1668 ast_channel_unref(logged);
1670 if (agent->logged != logged) {
1676 ast_bridge_channel_lock(bc);
1677 if (bc->chan != logged || agent->logged != logged) {
1678 ast_bridge_channel_unlock(bc);
1686 static void caller_abort_agent(struct agent_pvt *agent)
1688 struct ast_bridge_channel *logged;
1690 logged = agent_bridge_channel_get_lock(agent);
1692 struct ast_bridge *caller_bridge;
1694 ast_debug(1, "Agent '%s' no longer logged in.\n", agent->username);
1697 caller_bridge = agent->caller_bridge;
1698 agent->caller_bridge = NULL;
1699 agent_unlock(agent);
1700 if (caller_bridge) {
1701 ast_bridge_destroy(caller_bridge);
1706 /* Kick the agent out of the holding bridge to reset it. */
1707 ast_bridge_channel_leave_bridge_nolock(logged);
1708 ast_bridge_channel_unlock(logged);
1711 static int caller_safety_timeout(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
1713 struct agent_pvt *agent = hook_pvt;
1715 if (agent->state == AGENT_STATE_CALL_PRESENT) {
1716 ast_verb(3, "Agent '%s' did not respond. Safety timeout.\n", agent->username);
1717 ast_bridge_channel_leave_bridge(bridge_channel);
1718 caller_abort_agent(agent);
1724 static void agent_alert(struct ast_bridge_channel *bridge_channel, const void *payload, size_t payload_size)
1726 const char *agent_id = payload;
1727 const char *playfile;
1728 RAII_VAR(struct agent_pvt *, agent, NULL, ao2_cleanup);
1730 agent = ao2_find(agents, agent_id, OBJ_KEY);
1732 ast_debug(1, "Agent '%s' does not exist. Where did it go?\n", agent_id);
1736 /* Alert the agent. */
1738 playfile = ast_strdupa(agent->cfg->beep_sound);
1739 agent_unlock(agent);
1740 ast_stream_and_wait(bridge_channel->chan, playfile, AST_DIGIT_NONE);
1743 switch (agent->state) {
1744 case AGENT_STATE_CALL_PRESENT:
1745 if (ast_test_flag(agent, AGENT_FLAG_ACK_CALL)
1746 ? agent->override_ack_call : agent->cfg->ack_call) {
1747 agent->state = AGENT_STATE_CALL_WAIT_ACK;
1748 agent->ack_time = ast_tvnow();
1752 /* Connect to caller now. */
1753 ast_debug(1, "Agent %s: Immediately connecting to call.\n", agent->username);
1754 agent_connect_caller(bridge_channel, agent);/* Will unlock agent. */
1759 agent_unlock(agent);
1762 static int send_alert_to_agent(struct ast_bridge_channel *bridge_channel, const char *agent_id)
1764 return ast_bridge_channel_queue_callback(bridge_channel, agent_alert, agent_id,
1765 strlen(agent_id) + 1);
1768 static int send_colp_to_agent(struct ast_bridge_channel *bridge_channel, struct ast_party_connected_line *connected)
1770 struct ast_set_party_connected_line update = {
1775 unsigned char data[1024]; /* This should be large enough */
1778 datalen = ast_connected_line_build_data(data, sizeof(data), connected, &update);
1779 if (datalen == (size_t) -1) {
1783 return ast_bridge_channel_queue_control_data(bridge_channel,
1784 AST_CONTROL_CONNECTED_LINE, data, datalen);
1788 * \brief Dialplan AgentRequest application to locate an agent to talk with.
1790 * \param chan Channel wanting to talk with an agent.
1791 * \param data Application parameters
1793 * \retval 0 To continue in dialplan.
1794 * \retval -1 To hangup.
1796 static int agent_request_exec(struct ast_channel *chan, const char *data)
1798 struct ast_bridge *caller_bridge;
1799 struct ast_bridge_channel *logged;
1802 struct ast_bridge_features caller_features;
1803 struct ast_party_connected_line connected;
1804 AST_DECLARE_APP_ARGS(args,
1805 AST_APP_ARG(agent_id);
1806 AST_APP_ARG(other); /* Any remaining unused arguments */
1809 RAII_VAR(struct agent_pvt *, agent, NULL, ao2_cleanup);
1811 if (bridge_agent_hold_deferred_create()) {
1815 parse = ast_strdupa(data);
1816 AST_STANDARD_APP_ARGS(args, parse);
1818 if (ast_strlen_zero(args.agent_id)) {
1819 ast_log(LOG_WARNING, "AgentRequest requires an AgentId\n");
1823 /* Find the agent. */
1824 agent = ao2_find(agents, args.agent_id, OBJ_KEY);
1826 ast_verb(3, "Agent '%s' does not exist.\n", args.agent_id);
1827 pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "INVALID");
1831 if (ast_bridge_features_init(&caller_features)) {
1835 /* Add safety timeout hook. */
1837 if (ast_bridge_interval_hook(&caller_features, CALLER_SAFETY_TIMEOUT_TIME,
1838 caller_safety_timeout, agent, __ao2_cleanup, AST_BRIDGE_HOOK_REMOVE_ON_PULL)) {
1840 ast_bridge_features_cleanup(&caller_features);
1844 caller_bridge = ast_bridge_basic_new();
1845 if (!caller_bridge) {
1846 ast_bridge_features_cleanup(&caller_features);
1850 /* Get COLP for agent. */
1851 ast_party_connected_line_init(&connected);
1852 ast_channel_lock(chan);
1853 ast_connected_line_copy_from_caller(&connected, ast_channel_caller(chan));
1854 ast_channel_unlock(chan);
1857 switch (agent->state) {
1858 case AGENT_STATE_LOGGED_OUT:
1859 case AGENT_STATE_LOGGING_OUT:
1860 agent_unlock(agent);
1861 ast_party_connected_line_free(&connected);
1862 ast_bridge_destroy(caller_bridge);
1863 ast_bridge_features_cleanup(&caller_features);
1864 ast_verb(3, "Agent '%s' not logged in.\n", agent->username);
1865 pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "NOT_LOGGED_IN");
1867 case AGENT_STATE_READY_FOR_CALL:
1868 ao2_ref(caller_bridge, +1);
1869 agent->caller_bridge = caller_bridge;
1870 agent->state = AGENT_STATE_CALL_PRESENT;
1871 agent->devstate = AST_DEVICE_INUSE;
1874 agent_unlock(agent);
1875 ast_party_connected_line_free(&connected);
1876 ast_bridge_destroy(caller_bridge);
1877 ast_bridge_features_cleanup(&caller_features);
1878 ast_verb(3, "Agent '%s' is busy.\n", agent->username);
1879 pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "BUSY");
1882 agent_unlock(agent);
1883 agent_devstate_changed(agent->username);
1885 logged = agent_bridge_channel_get_lock(agent);
1887 ast_party_connected_line_free(&connected);
1888 ast_bridge_destroy(caller_bridge);
1889 ast_bridge_features_cleanup(&caller_features);
1890 ast_verb(3, "Agent '%s' not logged in.\n", agent->username);
1891 pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "NOT_LOGGED_IN");
1892 caller_abort_agent(agent);
1896 send_colp_to_agent(logged, &connected);
1897 ast_party_connected_line_free(&connected);
1899 res = send_alert_to_agent(logged, agent->username);
1900 ast_bridge_channel_unlock(logged);
1901 ao2_ref(logged, -1);
1903 ast_bridge_destroy(caller_bridge);
1904 ast_bridge_features_cleanup(&caller_features);
1905 ast_verb(3, "Agent '%s': Failed to alert the agent.\n", agent->username);
1906 pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "ERROR");
1907 caller_abort_agent(agent);
1911 ast_indicate(chan, AST_CONTROL_RINGING);
1912 ast_bridge_join(caller_bridge, chan, NULL, &caller_features, NULL, 1);
1913 ast_bridge_features_cleanup(&caller_features);
1920 * \brief Get agent config values from the login channel.
1923 * \param agent What to setup channel config values on.
1924 * \param chan Channel logging in as an agent.
1928 static void agent_login_channel_config(struct agent_pvt *agent, struct ast_channel *chan)
1930 struct ast_flags opts = { 0 };
1931 struct ast_party_connected_line connected;
1932 unsigned int override_ack_call = 0;
1933 unsigned int override_auto_logoff = 0;
1934 unsigned int override_wrapup_time = 0;
1935 const char *override_dtmf_accept = NULL;
1938 ast_party_connected_line_init(&connected);
1940 /* Get config values from channel. */
1941 ast_channel_lock(chan);
1942 ast_party_connected_line_copy(&connected, ast_channel_connected(chan));
1944 var = pbx_builtin_getvar_helper(chan, "AGENTACKCALL");
1945 if (!ast_strlen_zero(var)) {
1946 override_ack_call = ast_true(var) ? 1 : 0;
1947 ast_set_flag(&opts, AGENT_FLAG_ACK_CALL);
1950 var = pbx_builtin_getvar_helper(chan, "AGENTACCEPTDTMF");
1951 if (!ast_strlen_zero(var)) {
1952 override_dtmf_accept = ast_strdupa(var);
1953 ast_set_flag(&opts, AGENT_FLAG_DTMF_ACCEPT);
1956 var = pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF");
1957 if (!ast_strlen_zero(var)) {
1958 if (sscanf(var, "%u", &override_auto_logoff) == 1) {
1959 ast_set_flag(&opts, AGENT_FLAG_AUTO_LOGOFF);
1963 var = pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME");
1964 if (!ast_strlen_zero(var)) {
1965 if (sscanf(var, "%u", &override_wrapup_time) == 1) {
1966 ast_set_flag(&opts, AGENT_FLAG_WRAPUP_TIME);
1969 ast_channel_unlock(chan);
1971 /* Set config values on agent. */
1973 ast_party_connected_line_free(&agent->waiting_colp);
1974 agent->waiting_colp = connected;
1976 ast_string_field_set(agent, override_dtmf_accept, override_dtmf_accept);
1977 ast_copy_flags(agent, &opts, AST_FLAGS_ALL);
1978 agent->override_auto_logoff = override_auto_logoff;
1979 agent->override_wrapup_time = override_wrapup_time;
1980 agent->override_ack_call = override_ack_call;
1981 agent_unlock(agent);
1984 enum AGENT_LOGIN_OPT_FLAGS {
1985 OPT_SILENT = (1 << 0),
1987 AST_APP_OPTIONS(agent_login_opts, BEGIN_OPTIONS
1988 AST_APP_OPTION('s', OPT_SILENT),
1992 * \brief Dialplan AgentLogin application to log in an agent.
1994 * \param chan Channel attempting to login as an agent.
1995 * \param data Application parameters
1997 * \retval 0 To continue in dialplan.
1998 * \retval -1 To hangup.
2000 static int agent_login_exec(struct ast_channel *chan, const char *data)
2003 struct ast_flags opts;
2004 AST_DECLARE_APP_ARGS(args,
2005 AST_APP_ARG(agent_id);
2006 AST_APP_ARG(options);
2007 AST_APP_ARG(other); /* Any remaining unused arguments */
2010 RAII_VAR(struct agent_pvt *, agent, NULL, ao2_cleanup);
2012 if (bridge_agent_hold_deferred_create()) {
2016 if (ast_channel_state(chan) != AST_STATE_UP && ast_answer(chan)) {
2020 parse = ast_strdupa(data);
2021 AST_STANDARD_APP_ARGS(args, parse);
2023 if (ast_strlen_zero(args.agent_id)) {
2024 ast_log(LOG_WARNING, "AgentLogin requires an AgentId\n");
2028 if (ast_app_parse_options(agent_login_opts, &opts, NULL, args.options)) {
2029 /* General invalid option syntax. */
2033 /* Find the agent. */
2034 agent = ao2_find(agents, args.agent_id, OBJ_KEY);
2036 ast_verb(3, "Agent '%s' does not exist.\n", args.agent_id);
2037 pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "INVALID");
2041 /* Has someone already logged in as this agent already? */
2043 if (agent->logged) {
2044 agent_unlock(agent);
2045 ast_verb(3, "Agent '%s' already logged in.\n", agent->username);
2046 pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "ALREADY_LOGGED_IN");
2049 agent->logged = ast_channel_ref(chan);
2050 agent->last_disconnect = ast_tvnow();
2051 time(&agent->login_start);
2052 agent_unlock(agent);
2054 agent_login_channel_config(agent, chan);
2056 if (!ast_test_flag(&opts, OPT_SILENT)
2057 && !ast_streamfile(chan, "agent-loginok", ast_channel_language(chan))) {
2058 ast_waitstream(chan, "");
2061 ast_verb(2, "Agent '%s' logged in (format %s/%s)\n", agent->username,
2062 ast_getformatname(ast_channel_readformat(chan)),
2063 ast_getformatname(ast_channel_writeformat(chan)));
2064 send_agent_login(chan, agent->username);
2066 agent_run(agent, chan);
2070 static int agent_function_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
2073 struct agent_pvt *agent;
2074 struct ast_channel *logged;
2075 AST_DECLARE_APP_ARGS(args,
2076 AST_APP_ARG(agentid);
2082 parse = ast_strdupa(data ?: "");
2083 AST_NONSTANDARD_APP_ARGS(args, parse, ':');
2085 if (ast_strlen_zero(args.agentid)) {
2086 ast_log(LOG_WARNING, "The AGENT function requires an argument - agentid!\n");
2090 args.item = "status";
2093 agent = ao2_find(agents, args.agentid, OBJ_KEY);
2095 ast_log(LOG_WARNING, "Agent '%s' not found!\n", args.agentid);
2100 if (!strcasecmp(args.item, "status")) {
2103 if (agent->logged) {
2104 status = "LOGGEDIN";
2106 status = "LOGGEDOUT";
2108 ast_copy_string(buf, status, len);
2109 } else if (!strcasecmp(args.item, "name")) {
2110 ast_copy_string(buf, agent->cfg->full_name, len);
2111 } else if (!strcasecmp(args.item, "mohclass")) {
2112 ast_copy_string(buf, agent->cfg->moh, len);
2113 } else if (!strcasecmp(args.item, "channel")) {
2114 logged = agent_lock_logged(agent);
2118 ast_copy_string(buf, ast_channel_name(logged), len);
2119 ast_channel_unlock(logged);
2120 ast_channel_unref(logged);
2122 pos = strrchr(buf, '-');
2127 } else if (!strcasecmp(args.item, "fullchannel")) {
2128 logged = agent_lock_logged(agent);
2130 ast_copy_string(buf, ast_channel_name(logged), len);
2131 ast_channel_unlock(logged);
2132 ast_channel_unref(logged);
2135 agent_unlock(agent);
2141 static struct ast_custom_function agent_function = {
2143 .read = agent_function_read,
2146 struct agent_complete {
2147 /*! Nth match to return. */
2149 /*! Which match currently on. */
2153 static int complete_agent_search(void *obj, void *arg, void *data, int flags)
2155 struct agent_complete *search = data;
2157 if (++search->which > search->state) {
2163 static char *complete_agent(const char *word, int state)
2166 struct agent_pvt *agent;
2167 struct agent_complete search = {
2171 agent = ao2_callback_data(agents, ast_strlen_zero(word) ? 0 : OBJ_PARTIAL_KEY,
2172 complete_agent_search, (char *) word, &search);
2176 ret = ast_strdup(agent->username);
2181 static int complete_agent_logoff_search(void *obj, void *arg, void *data, int flags)
2183 struct agent_pvt *agent = obj;
2184 struct agent_complete *search = data;
2186 if (!agent->logged) {
2189 if (++search->which > search->state) {
2195 static char *complete_agent_logoff(const char *word, int state)
2198 struct agent_pvt *agent;
2199 struct agent_complete search = {
2203 agent = ao2_callback_data(agents, ast_strlen_zero(word) ? 0 : OBJ_PARTIAL_KEY,
2204 complete_agent_logoff_search, (char *) word, &search);
2208 ret = ast_strdup(agent->username);
2213 static void agent_show_requested(struct ast_cli_args *a, int online_only)
2215 #define FORMAT_HDR "%-8s %-20s %-11s %-30s %s\n"
2216 #define FORMAT_ROW "%-8s %-20s %-11s %-30s %s\n"
2218 struct ao2_iterator iter;
2219 struct agent_pvt *agent;
2220 struct ast_str *out = ast_str_alloca(512);
2221 unsigned int agents_total = 0;
2222 unsigned int agents_logged_in = 0;
2223 unsigned int agents_talking = 0;
2225 ast_cli(a->fd, FORMAT_HDR, "Agent-ID", "Name", "State", "Channel", "Talking with");
2226 iter = ao2_iterator_init(agents, 0);
2227 for (; (agent = ao2_iterator_next(&iter)); ao2_ref(agent, -1)) {
2228 struct ast_channel *logged;
2233 logged = agent_lock_logged(agent);
2235 const char *talking_with;
2239 talking_with = pbx_builtin_getvar_helper(logged, "BRIDGEPEER");
2240 if (!ast_strlen_zero(talking_with)) {
2245 ast_str_set(&out, 0, FORMAT_ROW, agent->username, agent->cfg->full_name,
2246 ast_devstate_str(agent->devstate), ast_channel_name(logged), talking_with);
2247 ast_channel_unlock(logged);
2248 ast_channel_unref(logged);
2250 ast_str_set(&out, 0, FORMAT_ROW, agent->username, agent->cfg->full_name,
2251 ast_devstate_str(agent->devstate), "", "");
2253 agent_unlock(agent);
2255 if (!online_only || logged) {
2256 ast_cli(a->fd, "%s", ast_str_buffer(out));
2259 ao2_iterator_destroy(&iter);
2261 ast_cli(a->fd, "\nDefined agents: %u, Logged in: %u, Talking: %u\n",
2262 agents_total, agents_logged_in, agents_talking);
2268 static char *agent_handle_show_online(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2272 e->command = "agent show online";
2274 "Usage: agent show online\n"
2275 " Provides summary information for logged in agents.\n";
2282 return CLI_SHOWUSAGE;
2285 agent_show_requested(a, 1);
2290 static char *agent_handle_show_all(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2294 e->command = "agent show all";
2296 "Usage: agent show all\n"
2297 " Provides summary information for all agents.\n";
2304 return CLI_SHOWUSAGE;
2307 agent_show_requested(a, 0);
2312 static char *agent_handle_show_specific(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2314 struct agent_pvt *agent;
2315 struct ast_channel *logged;
2316 struct ast_str *out = ast_str_alloca(4096);
2320 e->command = "agent show";
2322 "Usage: agent show <agent-id>\n"
2323 " Show information about the <agent-id> agent\n";
2327 return complete_agent(a->word, a->n);
2333 return CLI_SHOWUSAGE;
2336 agent = ao2_find(agents, a->argv[2], OBJ_KEY);
2338 ast_cli(a->fd, "Agent '%s' not found\n", a->argv[2]);
2343 logged = agent_lock_logged(agent);
2344 ast_str_set(&out, 0, "Id: %s\n", agent->username);
2345 ast_str_append(&out, 0, "Name: %s\n", agent->cfg->full_name);
2346 ast_str_append(&out, 0, "Beep: %s\n", agent->cfg->beep_sound);
2347 ast_str_append(&out, 0, "MOH: %s\n", agent->cfg->moh);
2348 ast_str_append(&out, 0, "RecordCalls: %s\n", AST_CLI_YESNO(agent->cfg->record_agent_calls));
2349 ast_str_append(&out, 0, "State: %s\n", ast_devstate_str(agent->devstate));
2351 const char *talking_with;
2353 ast_str_append(&out, 0, "LoggedInChannel: %s\n", ast_channel_name(logged));
2354 ast_str_append(&out, 0, "LoggedInTime: %ld\n", (long) agent->login_start);
2355 talking_with = pbx_builtin_getvar_helper(logged, "BRIDGEPEER");
2356 if (!ast_strlen_zero(talking_with)) {
2357 ast_str_append(&out, 0, "TalkingWith: %s\n", talking_with);
2358 ast_str_append(&out, 0, "CallStarted: %ld\n", (long) agent->call_start);
2360 ast_channel_unlock(logged);
2361 ast_channel_unref(logged);
2363 agent_unlock(agent);
2366 ast_cli(a->fd, "%s", ast_str_buffer(out));
2371 static char *agent_handle_logoff_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2375 e->command = "agent logoff";
2377 "Usage: agent logoff <agent-id> [soft]\n"
2378 " Sets an agent as no longer logged in.\n"
2379 " If 'soft' is specified, do not hangup existing calls.\n";
2383 return complete_agent_logoff(a->word, a->n);
2384 } else if (a->pos == 3 && a->n == 0
2385 && (ast_strlen_zero(a->word)
2386 || !strncasecmp("soft", a->word, strlen(a->word)))) {
2387 return ast_strdup("soft");
2392 if (a->argc < 3 || 4 < a->argc) {
2393 return CLI_SHOWUSAGE;
2395 if (a->argc == 4 && strcasecmp(a->argv[3], "soft")) {
2396 return CLI_SHOWUSAGE;
2399 if (!agent_logoff_request(a->argv[2], a->argc == 4)) {
2400 ast_cli(a->fd, "Logging out %s\n", a->argv[2]);
2406 static struct ast_cli_entry cli_agents[] = {
2407 AST_CLI_DEFINE(agent_handle_show_online, "Show status of online agents"),
2408 AST_CLI_DEFINE(agent_handle_show_all, "Show status of all agents"),
2409 AST_CLI_DEFINE(agent_handle_show_specific, "Show information about an agent"),
2410 AST_CLI_DEFINE(agent_handle_logoff_cmd, "Sets an agent offline"),
2413 static int action_agents(struct mansession *s, const struct message *m)
2415 const char *id = astman_get_header(m, "ActionID");
2416 char id_text[AST_MAX_BUF];
2417 struct ao2_iterator iter;
2418 struct agent_pvt *agent;
2419 struct ast_str *out = ast_str_alloca(4096);
2421 if (!ast_strlen_zero(id)) {
2422 snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", id);
2426 astman_send_ack(s, m, "Agents will follow");
2428 iter = ao2_iterator_init(agents, 0);
2429 for (; (agent = ao2_iterator_next(&iter)); ao2_ref(agent, -1)) {
2430 struct ast_channel *logged;
2433 logged = agent_lock_logged(agent);
2437 * AGENT_LOGGEDOFF - Agent isn't logged in
2438 * AGENT_IDLE - Agent is logged in, and waiting for call
2439 * AGENT_ONCALL - Agent is logged in, and on a call
2440 * AGENT_UNKNOWN - Don't know anything about agent. Shouldn't ever get this.
2442 ast_str_set(&out, 0, "Agent: %s\r\n", agent->username);
2443 ast_str_append(&out, 0, "Name: %s\r\n", agent->cfg->full_name);
2446 const char *talking_to_chan;
2447 struct ast_str *logged_headers;
2448 RAII_VAR(struct ast_channel_snapshot *, logged_snapshot, ast_channel_snapshot_create(logged), ao2_cleanup);
2450 if (!logged_snapshot
2451 || !(logged_headers =
2452 ast_manager_build_channel_state_string(logged_snapshot))) {
2453 ast_channel_unlock(logged);
2454 ast_channel_unref(logged);
2455 agent_unlock(agent);
2459 talking_to_chan = pbx_builtin_getvar_helper(logged, "BRIDGEPEER");
2460 if (!ast_strlen_zero(talking_to_chan)) {
2461 ast_str_append(&out, 0, "Status: %s\r\n", "AGENT_ONCALL");
2462 ast_str_append(&out, 0, "TalkingToChan: %s\r\n", talking_to_chan);
2463 ast_str_append(&out, 0, "CallStarted: %ld\n", (long) agent->call_start);
2465 ast_str_append(&out, 0, "Status: %s\r\n", "AGENT_IDLE");
2467 ast_str_append(&out, 0, "LoggedInTime: %ld\r\n", (long) agent->login_start);
2468 ast_str_append(&out, 0, "%s", ast_str_buffer(logged_headers));
2469 ast_channel_unlock(logged);
2470 ast_channel_unref(logged);
2471 ast_free(logged_headers);
2473 ast_str_append(&out, 0, "Status: %s\r\n", "AGENT_LOGGEDOFF");
2476 agent_unlock(agent);
2478 astman_append(s, "Event: Agents\r\n"
2480 ast_str_buffer(out), id_text);
2482 ao2_iterator_destroy(&iter);
2484 astman_append(s, "Event: AgentsComplete\r\n"
2490 static int action_agent_logoff(struct mansession *s, const struct message *m)
2492 const char *agent = astman_get_header(m, "Agent");
2493 const char *soft_s = astman_get_header(m, "Soft"); /* "true" is don't hangup */
2495 if (ast_strlen_zero(agent)) {
2496 astman_send_error(s, m, "No agent specified");
2500 if (!agent_logoff_request(agent, ast_true(soft_s))) {
2501 astman_send_ack(s, m, "Agent logged out");
2503 astman_send_error(s, m, "No such agent");
2509 static int unload_module(void)
2511 struct ast_bridge *holding;
2513 /* Unregister dialplan applications */
2514 ast_unregister_application(app_agent_login);
2515 ast_unregister_application(app_agent_request);
2517 /* Unregister dialplan functions */
2518 ast_custom_function_unregister(&agent_function);
2520 /* Unregister manager command */
2521 ast_manager_unregister("Agents");
2522 ast_manager_unregister("AgentLogoff");
2524 /* Unregister CLI commands */
2525 ast_cli_unregister_multiple(cli_agents, ARRAY_LEN(cli_agents));
2527 ast_devstate_prov_del("Agent");
2529 /* Destroy agent holding bridge. */
2530 holding = ao2_global_obj_replace(agent_holding, NULL);
2532 ast_bridge_destroy(holding);
2536 ao2_ref(agents, -1);
2541 static int load_module(void)
2545 agents = ao2_container_alloc_rbtree(AO2_ALLOC_OPT_LOCK_MUTEX,
2546 AO2_CONTAINER_ALLOC_OPT_DUPS_REPLACE, agent_pvt_sort_cmp, agent_pvt_cmp);
2548 return AST_MODULE_LOAD_FAILURE;
2550 if (load_config()) {
2551 ast_log(LOG_ERROR, "Unable to load config. Not loading module.\n");
2552 ao2_ref(agents, -1);
2554 return AST_MODULE_LOAD_DECLINE;
2557 /* Init agent holding bridge v_table. */
2558 bridging_init_agent_hold();
2560 /* Setup to provide Agent:agent-id device state. */
2561 res |= ast_devstate_prov_add("Agent", agent_pvt_devstate_get);
2564 res |= ast_cli_register_multiple(cli_agents, ARRAY_LEN(cli_agents));
2566 /* Manager commands */
2567 res |= ast_manager_register_xml("Agents", EVENT_FLAG_AGENT, action_agents);
2568 res |= ast_manager_register_xml("AgentLogoff", EVENT_FLAG_AGENT, action_agent_logoff);
2570 /* Dialplan Functions */
2571 res |= ast_custom_function_register(&agent_function);
2573 /* Dialplan applications */
2574 res |= ast_register_application_xml(app_agent_login, agent_login_exec);
2575 res |= ast_register_application_xml(app_agent_request, agent_request_exec);
2579 return AST_MODULE_LOAD_FAILURE;
2581 return AST_MODULE_LOAD_SUCCESS;
2584 static int reload(void)
2586 if (aco_process_config(&cfg_info, 1) == ACO_PROCESS_ERROR) {
2587 /* Just keep the config we already have in place. */
2593 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Call center agent pool applications",
2594 .load = load_module,
2595 .unload = unload_module,
2597 .load_pri = AST_MODPRI_DEVSTATE_PROVIDER,