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,
112 alert the agent, or acknowledge the call will continue in the dialplan
113 with <variable>AGENT_STATUS</variable> set.</para>
114 <para><variable>AGENT_STATUS</variable> enumeration values:</para>
116 <enum name = "INVALID"><para>The specified agent is invalid.</para></enum>
117 <enum name = "NOT_LOGGED_IN"><para>The agent is not available.</para></enum>
118 <enum name = "BUSY"><para>The agent is on another call.</para></enum>
119 <enum name = "NOT_CONNECTED"><para>The agent did not connect with the
120 call. The agent most likely did not acknowledge the call.</para></enum>
121 <enum name = "ERROR"><para>Alerting the agent failed.</para></enum>
125 <ref type="application">AgentLogin</ref>
128 <function name="AGENT" language="en_US">
130 Gets information about an Agent
133 <parameter name="AgentId" required="true" />
134 <parameter name="item">
135 <para>The valid items to retrieve are:</para>
138 <para>(default) The status of the agent (LOGGEDIN | LOGGEDOUT)</para>
140 <enum name="password">
141 <para>Deprecated. The dialplan handles any agent authentication.</para>
144 <para>The name of the agent</para>
146 <enum name="mohclass">
147 <para>MusicOnHold class</para>
149 <enum name="channel">
150 <para>The name of the active channel for the Agent (AgentLogin)</para>
152 <enum name="fullchannel">
153 <para>The untruncated name of the active channel for the Agent (AgentLogin)</para>
158 <description></description>
160 <manager name="Agents" language="en_US">
162 Lists agents and their status.
165 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
168 <para>Will list info about all defined agents.</para>
171 <ref type="managerEvent">Agents</ref>
172 <ref type="managerEvent">AgentsComplete</ref>
175 <managerEvent language="en_US" name="Agents">
176 <managerEventInstance class="EVENT_FLAG_AGENT">
178 Response event in a series to the Agents AMI action containing
179 information about a defined agent.
182 <parameter name="Agent">
183 <para>Agent ID of the agent.</para>
185 <parameter name="Name">
186 <para>User friendly name of the agent.</para>
188 <parameter name="Status">
189 <para>Current status of the agent.</para>
190 <para>The valid values are:</para>
192 <enum name="AGENT_LOGGEDOFF" />
193 <enum name="AGENT_IDLE" />
194 <enum name="AGENT_ONCALL" />
197 <parameter name="TalkingToChan">
198 <para><variable>BRIDGEPEER</variable> value on agent channel.</para>
199 <para>Present if Status value is <literal>AGENT_ONCALL</literal>.</para>
201 <parameter name="CallStarted">
202 <para>Epoche time when the agent started talking with the caller.</para>
203 <para>Present if Status value is <literal>AGENT_ONCALL</literal>.</para>
205 <parameter name="LoggedInTime">
206 <para>Epoche time when the agent logged in.</para>
207 <para>Present if Status value is <literal>AGENT_IDLE</literal> or <literal>AGENT_ONCALL</literal>.</para>
210 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
213 <para>The channel snapshot is present if the Status value is <literal>AGENT_IDLE</literal> or <literal>AGENT_ONCALL</literal>.</para>
216 <ref type="manager">Agents</ref>
218 </managerEventInstance>
220 <managerEvent language="en_US" name="AgentsComplete">
221 <managerEventInstance class="EVENT_FLAG_AGENT">
223 Final response event in a series of events to the Agents AMI action.
226 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
229 <ref type="manager">Agents</ref>
231 </managerEventInstance>
233 <manager name="AgentLogoff" language="en_US">
235 Sets an agent as no longer logged in.
238 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
239 <parameter name="Agent" required="true">
240 <para>Agent ID of the agent to log off.</para>
242 <parameter name="Soft">
243 <para>Set to <literal>true</literal> to not hangup existing calls.</para>
247 <para>Sets an agent as no longer logged in.</para>
250 <configInfo name="app_agent_pool" language="en_US">
251 <synopsis>Agent pool applications</synopsis>
253 <note><para>Option changes take effect on agent login or after an agent
254 disconnects from a call.</para></note>
256 <configFile name="agents.conf">
257 <configObject name="global">
258 <synopsis>Unused, but reserved.</synopsis>
260 <configObject name="agent-id">
261 <synopsis>Configure an agent for the pool.</synopsis>
263 <xi:include xpointer="xpointer(/docs/configInfo[@name='app_agent_pool']/description/note)" />
265 <configOption name="ackcall">
266 <synopsis>Enable to require the agent to acknowledge a call.</synopsis>
268 <para>Enable to require the agent to give a DTMF acknowledgement
269 when the agent receives a call.</para>
270 <note><para>The option is overridden by <variable>AGENTACKCALL</variable> on agent login.</para></note>
271 <xi:include xpointer="xpointer(/docs/configInfo[@name='app_agent_pool']/description/note)" />
274 <configOption name="acceptdtmf">
275 <synopsis>DTMF key sequence the agent uses to acknowledge a call.</synopsis>
277 <note><para>The option is overridden by <variable>AGENTACCEPTDTMF</variable> on agent login.</para></note>
278 <note><para>The option is ignored unless the ackcall option is enabled.</para></note>
279 <xi:include xpointer="xpointer(/docs/configInfo[@name='app_agent_pool']/description/note)" />
282 <configOption name="autologoff">
283 <synopsis>Time the agent has to acknowledge a call before being logged off.</synopsis>
285 <para>Set how many seconds a call for the agent has to wait for the
286 agent to acknowledge the call before the agent is automatically
287 logged off. If set to zero then the call will wait forever for
288 the agent to acknowledge.</para>
289 <note><para>The option is overridden by <variable>AGENTAUTOLOGOFF</variable> on agent login.</para></note>
290 <note><para>The option is ignored unless the ackcall option is enabled.</para></note>
291 <xi:include xpointer="xpointer(/docs/configInfo[@name='app_agent_pool']/description/note)" />
294 <configOption name="wrapuptime">
295 <synopsis>Minimum time the agent has between calls.</synopsis>
297 <para>Set the minimum amount of time in milliseconds after
298 disconnecting a call before the agent can receive a new call.</para>
299 <note><para>The option is overridden by <variable>AGENTWRAPUPTIME</variable> on agent login.</para></note>
300 <xi:include xpointer="xpointer(/docs/configInfo[@name='app_agent_pool']/description/note)" />
303 <configOption name="musiconhold">
304 <synopsis>Music on hold class the agent listens to between calls.</synopsis>
306 <xi:include xpointer="xpointer(/docs/configInfo[@name='app_agent_pool']/description/note)" />
309 <configOption name="recordagentcalls">
310 <synopsis>Enable to automatically record calls the agent takes.</synopsis>
312 <para>Enable recording calls the agent takes automatically by
313 invoking the automixmon DTMF feature when the agent connects
314 to a caller. See <filename>features.conf.sample</filename> for information about
315 the automixmon feature.</para>
316 <xi:include xpointer="xpointer(/docs/configInfo[@name='app_agent_pool']/description/note)" />
319 <configOption name="custom_beep">
320 <synopsis>Sound file played to alert the agent when a call is present.</synopsis>
322 <xi:include xpointer="xpointer(/docs/configInfo[@name='app_agent_pool']/description/note)" />
325 <configOption name="fullname">
326 <synopsis>A friendly name for the agent used in log messages.</synopsis>
328 <xi:include xpointer="xpointer(/docs/configInfo[@name='app_agent_pool']/description/note)" />
336 /* ------------------------------------------------------------------- */
338 #define AST_MAX_BUF 256
340 /*! Maximum wait time (in ms) for the custom_beep file to play announcing the caller. */
341 #define CALLER_SAFETY_TIMEOUT_TIME (2 * 60 * 1000)
343 /*! Number of seconds to wait for local channel optimizations to complete. */
344 #define LOGIN_WAIT_TIMEOUT_TIME 5
346 static const char app_agent_login[] = "AgentLogin";
347 static const char app_agent_request[] = "AgentRequest";
349 /*! Agent config parameters. */
351 AST_DECLARE_STRING_FIELDS(
352 /*! Identification of the agent. (agents config container key) */
353 AST_STRING_FIELD(username);
354 /*! Name of agent for logging and querying purposes */
355 AST_STRING_FIELD(full_name);
358 * \brief DTMF string for an agent to accept a call.
360 * \note The channel variable AGENTACCEPTDTMF overrides on login.
362 AST_STRING_FIELD(dtmf_accept);
363 /*! Beep sound file to use. Alert the agent a call is waiting. */
364 AST_STRING_FIELD(beep_sound);
365 /*! MOH class to use while agent waiting for call. */
366 AST_STRING_FIELD(moh);
369 * \brief Number of seconds for agent to ack a call before being logged off.
371 * \note The channel variable AGENTAUTOLOGOFF overrides on login.
372 * \note If zero then timer is disabled.
374 unsigned int auto_logoff;
376 * \brief Time after a call in ms before the agent can get a new call.
378 * \note The channel variable AGENTWRAPUPTIME overrides on login.
380 unsigned int wrapup_time;
382 * \brief TRUE if agent needs to ack a call to accept it.
384 * \note The channel variable AGENTACKCALL overrides on login.
387 /*! TRUE if agent calls are automatically recorded. */
388 int record_agent_calls;
393 * \brief Agent config ao2 container sort function.
396 * \param obj_left pointer to the (user-defined part) of an object.
397 * \param obj_right pointer to the (user-defined part) of an object.
398 * \param flags flags from ao2_callback()
399 * OBJ_POINTER - if set, 'obj_right', is an object.
400 * OBJ_KEY - if set, 'obj_right', is a search key item that is not an object.
401 * OBJ_PARTIAL_KEY - if set, 'obj_right', is a partial search key item that is not an object.
403 * \retval <0 if obj_left < obj_right
404 * \retval =0 if obj_left == obj_right
405 * \retval >0 if obj_left > obj_right
407 static int agent_cfg_sort_cmp(const void *obj_left, const void *obj_right, int flags)
409 const struct agent_cfg *cfg_left = obj_left;
410 const struct agent_cfg *cfg_right = obj_right;
411 const char *right_key = obj_right;
414 switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
417 right_key = cfg_right->username;
420 cmp = strcmp(cfg_left->username, right_key);
422 case OBJ_PARTIAL_KEY:
423 cmp = strncmp(cfg_left->username, right_key, strlen(right_key));
429 static void agent_cfg_destructor(void *vdoomed)
431 struct agent_cfg *doomed = vdoomed;
433 ast_string_field_free_memory(doomed);
436 static void *agent_cfg_alloc(const char *name)
438 struct agent_cfg *cfg;
440 cfg = ao2_alloc_options(sizeof(*cfg), agent_cfg_destructor,
441 AO2_ALLOC_OPT_LOCK_NOLOCK);
442 if (!cfg || ast_string_field_init(cfg, 64)) {
445 ast_string_field_set(cfg, username, name);
449 static void *agent_cfg_find(struct ao2_container *agents, const char *username)
451 return ao2_find(agents, username, OBJ_KEY);
454 /*! Agents configuration */
456 /*! Master configured agents container. */
457 struct ao2_container *agents;
460 static struct aco_type agent_type = {
463 .category_match = ACO_BLACKLIST,
464 .category = "^(general|agents)$",
465 .item_alloc = agent_cfg_alloc,
466 .item_find = agent_cfg_find,
467 .item_offset = offsetof(struct agents_cfg, agents),
470 static struct aco_type *agent_types[] = ACO_TYPES(&agent_type);
472 /* The general category is reserved, but unused */
473 static struct aco_type general_type = {
476 .category_match = ACO_WHITELIST,
477 .category = "^general$",
480 static struct aco_file agents_conf = {
481 .filename = "agents.conf",
482 .types = ACO_TYPES(&general_type, &agent_type),
485 static AO2_GLOBAL_OBJ_STATIC(cfg_handle);
487 static void agents_cfg_destructor(void *vdoomed)
489 struct agents_cfg *doomed = vdoomed;
491 ao2_cleanup(doomed->agents);
492 doomed->agents = NULL;
497 * \brief Create struct agents_cfg object.
500 * \note A lock is not needed for the object or any secondary
501 * created cfg objects. These objects are immutable after the
502 * config is loaded and applied.
504 * \retval New struct agents_cfg object.
505 * \retval NULL on error.
507 static void *agents_cfg_alloc(void)
509 struct agents_cfg *cfg;
511 cfg = ao2_alloc_options(sizeof(*cfg), agents_cfg_destructor,
512 AO2_ALLOC_OPT_LOCK_NOLOCK);
516 cfg->agents = ao2_container_alloc_rbtree(AO2_ALLOC_OPT_LOCK_NOLOCK,
517 AO2_CONTAINER_ALLOC_OPT_DUPS_REJECT, agent_cfg_sort_cmp, NULL);
525 static void agents_post_apply_config(void);
527 CONFIG_INFO_STANDARD(cfg_info, cfg_handle, agents_cfg_alloc,
528 .files = ACO_FILES(&agents_conf),
529 .post_apply_config = agents_post_apply_config,
532 static void destroy_config(void)
534 ao2_global_obj_release(cfg_handle);
535 aco_info_destroy(&cfg_info);
538 static int load_config(void)
540 if (aco_info_init(&cfg_info)) {
545 aco_option_register(&cfg_info, "ackcall", ACO_EXACT, agent_types, "no", OPT_BOOL_T, 1, FLDSET(struct agent_cfg, ack_call));
546 aco_option_register(&cfg_info, "acceptdtmf", ACO_EXACT, agent_types, "#", OPT_STRINGFIELD_T, 1, STRFLDSET(struct agent_cfg, dtmf_accept));
547 aco_option_register(&cfg_info, "autologoff", ACO_EXACT, agent_types, "0", OPT_UINT_T, 0, FLDSET(struct agent_cfg, auto_logoff));
548 aco_option_register(&cfg_info, "wrapuptime", ACO_EXACT, agent_types, "0", OPT_UINT_T, 0, FLDSET(struct agent_cfg, wrapup_time));
549 aco_option_register(&cfg_info, "musiconhold", ACO_EXACT, agent_types, "default", OPT_STRINGFIELD_T, 0, STRFLDSET(struct agent_cfg, moh));
550 aco_option_register(&cfg_info, "recordagentcalls", ACO_EXACT, agent_types, "no", OPT_BOOL_T, 1, FLDSET(struct agent_cfg, record_agent_calls));
551 aco_option_register(&cfg_info, "custom_beep", ACO_EXACT, agent_types, "beep", OPT_STRINGFIELD_T, 0, STRFLDSET(struct agent_cfg, beep_sound));
552 aco_option_register(&cfg_info, "fullname", ACO_EXACT, agent_types, NULL, OPT_STRINGFIELD_T, 0, STRFLDSET(struct agent_cfg, full_name));
554 if (aco_process_config(&cfg_info, 0) == ACO_PROCESS_ERROR) {
562 /*! The agent is defined but an agent is not present. */
563 AGENT_STATE_LOGGED_OUT,
564 /*! Forced initial login wait to allow any local channel optimizations to happen. */
565 AGENT_STATE_PROBATION_WAIT,
566 /*! The agent is ready for a call. */
567 AGENT_STATE_READY_FOR_CALL,
568 /*! The agent has a call waiting to connect. */
569 AGENT_STATE_CALL_PRESENT,
570 /*! The agent needs to ack the call. */
571 AGENT_STATE_CALL_WAIT_ACK,
572 /*! The agent is connected with a call. */
574 /*! The agent is resting between calls. */
575 AGENT_STATE_CALL_WRAPUP,
576 /*! The agent is being kicked out. */
577 AGENT_STATE_LOGGING_OUT,
580 /*! Agent config option override flags. */
581 enum agent_override_flags {
582 AGENT_FLAG_ACK_CALL = (1 << 0),
583 AGENT_FLAG_DTMF_ACCEPT = (1 << 1),
584 AGENT_FLAG_AUTO_LOGOFF = (1 << 2),
585 AGENT_FLAG_WRAPUP_TIME = (1 << 3),
588 /*! \brief Structure representing an agent. */
590 AST_DECLARE_STRING_FIELDS(
591 /*! Identification of the agent. (agents container key) */
592 AST_STRING_FIELD(username);
593 /*! Login override DTMF string for an agent to accept a call. */
594 AST_STRING_FIELD(override_dtmf_accept);
596 /*! Connected line information to send when reentering the holding bridge. */
597 struct ast_party_connected_line waiting_colp;
598 /*! Flags show if settings were overridden by channel vars. */
600 /*! Login override number of seconds for agent to ack a call before being logged off. */
601 unsigned int override_auto_logoff;
602 /*! Login override time after a call in ms before the agent can get a new call. */
603 unsigned int override_wrapup_time;
604 /*! Login override if agent needs to ack a call to accept it. */
605 unsigned int override_ack_call:1;
607 /*! TRUE if the agent is requested to logoff when the current call ends. */
608 unsigned int deferred_logoff:1;
610 /*! Mark and sweep config update to determine if an agent is dead. */
611 unsigned int the_mark:1;
613 * \brief TRUE if the agent is no longer configured and is being destroyed.
615 * \note Agents cannot log in if they are dead.
619 /*! Agent control state variable. */
620 enum agent_state state;
621 /*! Custom device state of agent. */
622 enum ast_device_state devstate;
624 /*! When agent first logged in */
626 /*! When agent login probation started. */
627 time_t probation_start;
628 /*! When call started */
630 /*! When ack timer started */
631 struct timeval ack_time;
632 /*! When last disconnected */
633 struct timeval last_disconnect;
635 /*! Caller is waiting in this bridge for agent to join. (Holds ref) */
636 struct ast_bridge *caller_bridge;
637 /*! Agent is logged in with this channel. (Holds ref) (NULL if not logged in.) */
638 struct ast_channel *logged;
639 /*! Active config values from config file. (Holds ref) */
640 struct agent_cfg *cfg;
643 /*! Container of defined agents. */
644 static struct ao2_container *agents;
647 * \brief Lock the agent.
649 * \param agent Agent to lock
653 #define agent_lock(agent) _agent_lock(agent, __FILE__, __PRETTY_FUNCTION__, __LINE__, #agent)
654 static inline void _agent_lock(struct agent_pvt *agent, const char *file, const char *function, int line, const char *var)
656 __ao2_lock(agent, AO2_LOCK_REQ_MUTEX, file, function, line, var);
660 * \brief Unlock the agent.
662 * \param agent Agent to unlock
666 #define agent_unlock(agent) _agent_unlock(agent, __FILE__, __PRETTY_FUNCTION__, __LINE__, #agent)
667 static inline void _agent_unlock(struct agent_pvt *agent, const char *file, const char *function, int line, const char *var)
669 __ao2_unlock(agent, file, function, line, var);
674 * \brief Obtain the agent logged in channel lock if it exists.
677 * \param agent Pointer to the LOCKED agent_pvt.
679 * \note Assumes the agent lock is already obtained.
681 * \note Defined locking order is channel lock then agent lock.
685 static struct ast_channel *agent_lock_logged(struct agent_pvt *agent)
687 struct ast_channel *logged;
690 if (!agent->logged) { /* No owner. Nothing to do. */
694 /* If we don't ref the logged, it could be killed when we unlock the agent. */
695 logged = ast_channel_ref(agent->logged);
697 /* Locking logged requires us to lock channel, then agent. */
699 ast_channel_lock(logged);
702 /* Check if logged changed during agent unlock period */
703 if (logged != agent->logged) {
704 /* Channel changed. Unref and do another pass. */
705 ast_channel_unlock(logged);
706 ast_channel_unref(logged);
708 /* Channel stayed the same. Return it. */
716 * \brief Get the Agent:agent_id device state.
719 * \param agent_id Username of the agent.
722 * Search the agents container for the agent and return the
725 * \return Device state of the agent.
727 static enum ast_device_state agent_pvt_devstate_get(const char *agent_id)
729 enum ast_device_state dev_state = AST_DEVICE_INVALID;
730 struct agent_pvt *agent;
732 agent = ao2_find(agents, agent_id, OBJ_KEY);
735 dev_state = agent->devstate;
744 * \brief Request an agent device state be updated.
747 * \param agent_id Which agent needs the device state updated.
751 static void agent_devstate_changed(const char *agent_id)
753 ast_devstate_changed(AST_DEVICE_UNKNOWN, AST_DEVSTATE_CACHABLE, "Agent:%s", agent_id);
756 static void agent_pvt_destructor(void *vdoomed)
758 struct agent_pvt *doomed = vdoomed;
760 /* Make sure device state reflects agent destruction. */
761 if (!ast_strlen_zero(doomed->username)) {
762 ast_debug(1, "Agent %s: Destroyed.\n", doomed->username);
763 agent_devstate_changed(doomed->username);
766 ast_party_connected_line_free(&doomed->waiting_colp);
767 if (doomed->caller_bridge) {
768 ast_bridge_destroy(doomed->caller_bridge, 0);
769 doomed->caller_bridge = NULL;
771 if (doomed->logged) {
772 doomed->logged = ast_channel_unref(doomed->logged);
774 ao2_cleanup(doomed->cfg);
776 ast_string_field_free_memory(doomed);
779 static struct agent_pvt *agent_pvt_new(struct agent_cfg *cfg)
781 struct agent_pvt *agent;
783 agent = ao2_alloc(sizeof(*agent), agent_pvt_destructor);
787 if (ast_string_field_init(agent, 32)) {
791 ast_string_field_set(agent, username, cfg->username);
792 ast_party_connected_line_init(&agent->waiting_colp);
795 agent->devstate = AST_DEVICE_UNAVAILABLE;
801 * \brief Agents ao2 container sort function.
804 * \param obj_left pointer to the (user-defined part) of an object.
805 * \param obj_right pointer to the (user-defined part) of an object.
806 * \param flags flags from ao2_callback()
807 * OBJ_POINTER - if set, 'obj_right', is an object.
808 * OBJ_KEY - if set, 'obj_right', is a search key item that is not an object.
809 * OBJ_PARTIAL_KEY - if set, 'obj_right', is a partial search key item that is not an object.
811 * \retval <0 if obj_left < obj_right
812 * \retval =0 if obj_left == obj_right
813 * \retval >0 if obj_left > obj_right
815 static int agent_pvt_sort_cmp(const void *obj_left, const void *obj_right, int flags)
817 const struct agent_pvt *agent_left = obj_left;
818 const struct agent_pvt *agent_right = obj_right;
819 const char *right_key = obj_right;
822 switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
825 right_key = agent_right->username;
828 cmp = strcmp(agent_left->username, right_key);
830 case OBJ_PARTIAL_KEY:
831 cmp = strncmp(agent_left->username, right_key, strlen(right_key));
839 * \brief ao2_find() callback function.
843 * found = ao2_find(agents, agent, OBJ_POINTER);
844 * found = ao2_find(agents, "agent-id", OBJ_KEY);
845 * found = ao2_find(agents, agent->logged, 0);
847 static int agent_pvt_cmp(void *obj, void *arg, int flags)
849 const struct agent_pvt *agent = obj;
852 switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
855 case OBJ_PARTIAL_KEY:
859 if (agent->logged == arg) {
869 static int agent_mark(void *obj, void *arg, int flags)
871 struct agent_pvt *agent = obj;
879 static void agents_mark(void)
881 ao2_callback(agents, 0, agent_mark, NULL);
884 static int agent_sweep(void *obj, void *arg, int flags)
886 struct agent_pvt *agent = obj;
890 if (agent->the_mark) {
893 /* Unlink dead agents immediately. */
900 static void agents_sweep(void)
902 struct ao2_iterator *iter;
903 struct agent_pvt *agent;
904 struct ast_channel *logged;
906 iter = ao2_callback(agents, OBJ_MULTIPLE | OBJ_UNLINK, agent_sweep, NULL);
910 for (; (agent = ao2_iterator_next(iter)); ao2_ref(agent, -1)) {
913 logged = ast_channel_ref(agent->logged);
922 "Forced logoff of agent %s(%s). Agent no longer configured.\n",
923 agent->username, ast_channel_name(logged));
924 ast_softhangup(logged, AST_SOFTHANGUP_EXPLICIT);
925 ast_channel_unref(logged);
927 ao2_iterator_destroy(iter);
930 static void agents_post_apply_config(void)
932 struct ao2_iterator iter;
933 struct agent_cfg *cfg;
934 RAII_VAR(struct agents_cfg *, cfgs, ao2_global_obj_ref(cfg_handle), ao2_cleanup);
936 ast_assert(cfgs != NULL);
939 iter = ao2_iterator_init(cfgs->agents, 0);
940 for (; (cfg = ao2_iterator_next(&iter)); ao2_ref(cfg, -1)) {
941 RAII_VAR(struct agent_pvt *, agent, ao2_find(agents, cfg->username, OBJ_KEY), ao2_cleanup);
946 if (!agent->logged) {
947 struct agent_cfg *cfg_old;
949 /* Replace the config of agents not logged in. */
950 cfg_old = agent->cfg;
953 ao2_cleanup(cfg_old);
958 agent = agent_pvt_new(cfg);
962 ao2_link(agents, agent);
963 ast_debug(1, "Agent %s: Created.\n", agent->username);
964 agent_devstate_changed(agent->username);
966 ao2_iterator_destroy(&iter);
970 static int agent_logoff_request(const char *agent_id, int soft)
972 struct ast_channel *logged;
973 RAII_VAR(struct agent_pvt *, agent, ao2_find(agents, agent_id, OBJ_KEY), ao2_cleanup);
980 logged = agent_lock_logged(agent);
983 agent->deferred_logoff = 1;
985 ast_softhangup(logged, AST_SOFTHANGUP_EXPLICIT);
987 ast_channel_unlock(logged);
988 ast_channel_unref(logged);
994 /*! Agent holding bridge instance holder. */
995 static AO2_GLOBAL_OBJ_STATIC(agent_holding);
997 /*! Agent holding bridge deferred creation lock. */
998 AST_MUTEX_DEFINE_STATIC(agent_holding_lock);
1002 * \brief Callback to clear AGENT_STATUS on the caller channel.
1004 * \param bridge_channel Which channel to operate on.
1005 * \param payload Data to pass to the callback. (NULL if none).
1006 * \param payload_size Size of the payload if payload is non-NULL. A number otherwise.
1008 * \note The payload MUST NOT have any resources that need to be freed.
1012 static void clear_agent_status(struct ast_bridge_channel *bridge_channel, const void *payload, size_t payload_size)
1014 pbx_builtin_setvar_helper(bridge_channel->chan, "AGENT_STATUS", NULL);
1019 * \brief Connect the agent with the waiting caller.
1022 * \param bridge_channel Agent channel connecting to the caller.
1023 * \param agent Which agent is connecting to the caller.
1025 * \note The agent is locked on entry and not locked on exit.
1029 static void agent_connect_caller(struct ast_bridge_channel *bridge_channel, struct agent_pvt *agent)
1031 struct ast_bridge *caller_bridge;
1032 int record_agent_calls;
1035 record_agent_calls = agent->cfg->record_agent_calls;
1036 caller_bridge = agent->caller_bridge;
1037 agent->caller_bridge = NULL;
1038 agent->state = AGENT_STATE_ON_CALL;
1039 time(&agent->call_start);
1040 agent_unlock(agent);
1042 if (!caller_bridge) {
1044 ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END,
1045 AST_CAUSE_NORMAL_CLEARING);
1048 res = ast_bridge_move(caller_bridge, bridge_channel->bridge, bridge_channel->chan,
1052 ast_bridge_destroy(caller_bridge, 0);
1053 ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END,
1054 AST_CAUSE_NORMAL_CLEARING);
1057 res = ast_bridge_channel_write_control_data(bridge_channel, AST_CONTROL_ANSWER, NULL, 0)
1058 || ast_bridge_channel_write_callback(bridge_channel, 0, clear_agent_status, NULL, 0);
1061 ast_bridge_destroy(caller_bridge, 0);
1065 if (record_agent_calls) {
1066 struct ast_bridge_features_automixmonitor options = {
1067 .start_stop = AUTO_MONITOR_START,
1071 * The agent is in the new bridge so we can invoke the
1072 * mixmonitor hook to only start recording.
1074 ast_bridge_features_do(AST_BRIDGE_BUILTIN_AUTOMIXMON, bridge_channel, &options);
1077 ao2_t_ref(caller_bridge, -1, "Agent successfully in caller_bridge");
1080 static int bridge_agent_hold_ack(struct ast_bridge_channel *bridge_channel, void *hook_pvt)
1082 struct agent_pvt *agent = hook_pvt;
1085 switch (agent->state) {
1086 case AGENT_STATE_CALL_WAIT_ACK:
1087 /* Connect to caller now. */
1088 ast_debug(1, "Agent %s: Acked call.\n", agent->username);
1089 agent_connect_caller(bridge_channel, agent);/* Will unlock agent. */
1094 agent_unlock(agent);
1098 static int bridge_agent_hold_heartbeat(struct ast_bridge_channel *bridge_channel, void *hook_pvt)
1100 struct agent_pvt *agent = hook_pvt;
1101 int probation_timedout = 0;
1102 int ack_timedout = 0;
1103 int wrapup_timedout = 0;
1104 int deferred_logoff;
1105 unsigned int wrapup_time;
1106 unsigned int auto_logoff;
1109 deferred_logoff = agent->deferred_logoff;
1110 if (deferred_logoff) {
1111 agent->state = AGENT_STATE_LOGGING_OUT;
1114 switch (agent->state) {
1115 case AGENT_STATE_PROBATION_WAIT:
1116 probation_timedout =
1117 LOGIN_WAIT_TIMEOUT_TIME <= (time(NULL) - agent->probation_start);
1118 if (probation_timedout) {
1119 /* Now ready for a caller. */
1120 agent->state = AGENT_STATE_READY_FOR_CALL;
1121 agent->devstate = AST_DEVICE_NOT_INUSE;
1124 case AGENT_STATE_CALL_WAIT_ACK:
1125 /* Check ack call time. */
1126 auto_logoff = agent->cfg->auto_logoff;
1127 if (ast_test_flag(agent, AGENT_FLAG_AUTO_LOGOFF)) {
1128 auto_logoff = agent->override_auto_logoff;
1131 auto_logoff *= 1000;
1132 ack_timedout = ast_tvdiff_ms(ast_tvnow(), agent->ack_time) > auto_logoff;
1134 agent->state = AGENT_STATE_LOGGING_OUT;
1138 case AGENT_STATE_CALL_WRAPUP:
1139 /* Check wrapup time. */
1140 wrapup_time = agent->cfg->wrapup_time;
1141 if (ast_test_flag(agent, AGENT_FLAG_WRAPUP_TIME)) {
1142 wrapup_time = agent->override_wrapup_time;
1144 wrapup_timedout = ast_tvdiff_ms(ast_tvnow(), agent->last_disconnect) > wrapup_time;
1145 if (wrapup_timedout) {
1146 agent->state = AGENT_STATE_READY_FOR_CALL;
1147 agent->devstate = AST_DEVICE_NOT_INUSE;
1153 agent_unlock(agent);
1155 if (deferred_logoff) {
1156 ast_debug(1, "Agent %s: Deferred logoff.\n", agent->username);
1157 ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END,
1158 AST_CAUSE_NORMAL_CLEARING);
1159 } else if (probation_timedout) {
1160 ast_debug(1, "Agent %s: Login complete.\n", agent->username);
1161 agent_devstate_changed(agent->username);
1162 } else if (ack_timedout) {
1163 ast_debug(1, "Agent %s: Ack call timeout.\n", agent->username);
1164 ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END,
1165 AST_CAUSE_NORMAL_CLEARING);
1166 } else if (wrapup_timedout) {
1167 ast_debug(1, "Agent %s: Wrapup timeout. Ready for new call.\n", agent->username);
1168 agent_devstate_changed(agent->username);
1174 static void agent_after_bridge_cb(struct ast_channel *chan, void *data);
1175 static void agent_after_bridge_cb_failed(enum ast_bridge_after_cb_reason reason, void *data);
1179 * \brief ast_bridge agent_hold push method.
1182 * \param self Bridge to operate upon.
1183 * \param bridge_channel Bridge channel to push.
1184 * \param swap Bridge channel to swap places with if not NULL.
1186 * \note On entry, self is already locked.
1188 * \retval 0 on success
1189 * \retval -1 on failure
1191 static int bridge_agent_hold_push(struct ast_bridge *self, struct ast_bridge_channel *bridge_channel, struct ast_bridge_channel *swap)
1194 unsigned int wrapup_time;
1195 char dtmf[AST_FEATURE_MAX_LEN];
1196 struct ast_channel *chan;
1197 const char *moh_class;
1198 RAII_VAR(struct agent_pvt *, agent, NULL, ao2_cleanup);
1200 chan = bridge_channel->chan;
1202 agent = ao2_find(agents, swap ? swap->chan : chan, 0);
1204 /* Could not find the agent. */
1208 /* Setup agent entertainment */
1210 moh_class = ast_strdupa(agent->cfg->moh);
1211 agent_unlock(agent);
1212 res |= ast_channel_add_bridge_role(chan, "holding_participant");
1213 res |= ast_channel_set_bridge_role_option(chan, "holding_participant", "idle_mode", "musiconhold");
1214 res |= ast_channel_set_bridge_role_option(chan, "holding_participant", "moh_class", moh_class);
1216 /* Add DTMF acknowledge hook. */
1219 if (ast_test_flag(agent, AGENT_FLAG_ACK_CALL)
1220 ? agent->override_ack_call : agent->cfg->ack_call) {
1221 const char *dtmf_accept;
1223 dtmf_accept = ast_test_flag(agent, AGENT_FLAG_DTMF_ACCEPT)
1224 ? agent->override_dtmf_accept : agent->cfg->dtmf_accept;
1225 ast_copy_string(dtmf, dtmf_accept, sizeof(dtmf));
1227 agent_unlock(agent);
1228 if (!ast_strlen_zero(dtmf)) {
1230 if (ast_bridge_dtmf_hook(bridge_channel->features, dtmf, bridge_agent_hold_ack,
1231 agent, __ao2_cleanup, AST_BRIDGE_HOOK_REMOVE_ON_PULL)) {
1237 /* Add heartbeat interval hook. */
1239 if (ast_bridge_interval_hook(bridge_channel->features, 0, 1000,
1240 bridge_agent_hold_heartbeat, agent, __ao2_cleanup, AST_BRIDGE_HOOK_REMOVE_ON_PULL)) {
1245 res |= ast_bridge_base_v_table.push(self, bridge_channel, swap);
1247 ast_channel_remove_bridge_role(chan, "holding_participant");
1252 res = ast_bridge_set_after_callback(chan, agent_after_bridge_cb,
1253 agent_after_bridge_cb_failed, chan);
1255 ast_channel_remove_bridge_role(chan, "holding_participant");
1260 ast_channel_unref(agent->logged);
1261 agent->logged = ast_channel_ref(chan);
1262 agent_unlock(agent);
1265 * Kick the channel out so it can come back in fully controlled.
1266 * Otherwise, the after bridge callback will linger and the
1267 * agent will have some slightly different behavior in corner
1270 ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END,
1271 AST_CAUSE_NORMAL_CLEARING);
1276 switch (agent->state) {
1277 case AGENT_STATE_LOGGED_OUT:
1279 * \todo XXX the login probation time should be only if it is needed.
1281 * Need to determine if there are any local channels that can
1282 * optimize and wait until they actually do before leaving the
1283 * AGENT_STATE_PROBATION_WAIT state. For now, the blind
1284 * timer of LOGIN_WAIT_TIMEOUT_TIME will do.
1287 * Start the login probation timer.
1289 * We cannot handle an agent local channel optimization when the
1290 * agent is on a call. The optimization may kick the agent
1291 * channel we know about out of the call without our being able
1292 * to switch to the replacement channel. Get any agent local
1293 * channel optimization out of the way while the agent is in the
1296 time(&agent->probation_start);
1297 agent->state = AGENT_STATE_PROBATION_WAIT;
1298 agent_unlock(agent);
1300 case AGENT_STATE_PROBATION_WAIT:
1301 /* Restart the probation timer. */
1302 time(&agent->probation_start);
1303 agent_unlock(agent);
1305 case AGENT_STATE_READY_FOR_CALL:
1307 * Likely someone manually kicked us out of the holding bridge
1308 * and we came right back in.
1310 agent_unlock(agent);
1313 /* Unexpected agent state. */
1316 case AGENT_STATE_CALL_PRESENT:
1317 case AGENT_STATE_CALL_WAIT_ACK:
1318 agent->state = AGENT_STATE_READY_FOR_CALL;
1319 agent->devstate = AST_DEVICE_NOT_INUSE;
1320 agent_unlock(agent);
1321 ast_debug(1, "Agent %s: Call abort recovery complete.\n", agent->username);
1322 agent_devstate_changed(agent->username);
1324 case AGENT_STATE_ON_CALL:
1325 case AGENT_STATE_CALL_WRAPUP:
1326 wrapup_time = agent->cfg->wrapup_time;
1327 if (ast_test_flag(agent, AGENT_FLAG_WRAPUP_TIME)) {
1328 wrapup_time = agent->override_wrapup_time;
1331 agent->state = AGENT_STATE_CALL_WRAPUP;
1333 agent->state = AGENT_STATE_READY_FOR_CALL;
1334 agent->devstate = AST_DEVICE_NOT_INUSE;
1336 agent_unlock(agent);
1338 /* No wrapup time. */
1339 ast_debug(1, "Agent %s: Ready for new call.\n", agent->username);
1340 agent_devstate_changed(agent->username);
1350 * \brief ast_bridge agent_hold pull method.
1352 * \param self Bridge to operate upon.
1353 * \param bridge_channel Bridge channel to pull.
1356 * Remove any channel hooks controlled by the bridge. Release
1357 * any resources held by bridge_channel->bridge_pvt and release
1358 * bridge_channel->bridge_pvt.
1360 * \note On entry, self is already locked.
1364 static void bridge_agent_hold_pull(struct ast_bridge *self, struct ast_bridge_channel *bridge_channel)
1366 ast_channel_remove_bridge_role(bridge_channel->chan, "holding_participant");
1367 ast_bridge_base_v_table.pull(self, bridge_channel);
1371 * \brief The bridge is being dissolved.
1373 * \param self Bridge to operate upon.
1376 * The bridge is being dissolved. Remove any external
1377 * references to the bridge so it can be destroyed.
1379 * \note On entry, self must NOT be locked.
1383 static void bridge_agent_hold_dissolving(struct ast_bridge *self)
1385 ao2_global_obj_release(agent_holding);
1386 ast_bridge_base_v_table.dissolving(self);
1389 static struct ast_bridge_methods bridge_agent_hold_v_table;
1391 static struct ast_bridge *bridge_agent_hold_new(void)
1393 struct ast_bridge *bridge;
1395 bridge = bridge_alloc(sizeof(struct ast_bridge), &bridge_agent_hold_v_table);
1396 bridge = bridge_base_init(bridge, AST_BRIDGE_CAPABILITY_HOLDING,
1397 AST_BRIDGE_FLAG_MERGE_INHIBIT_TO | AST_BRIDGE_FLAG_MERGE_INHIBIT_FROM
1398 | AST_BRIDGE_FLAG_SWAP_INHIBIT_FROM | AST_BRIDGE_FLAG_TRANSFER_PROHIBITED,
1399 "AgentPool", NULL, NULL);
1400 bridge = bridge_register(bridge);
1404 static void bridge_init_agent_hold(void)
1406 /* Setup bridge agent_hold subclass v_table. */
1407 bridge_agent_hold_v_table = ast_bridge_base_v_table;
1408 bridge_agent_hold_v_table.name = "agent_hold";
1409 bridge_agent_hold_v_table.dissolving = bridge_agent_hold_dissolving;
1410 bridge_agent_hold_v_table.push = bridge_agent_hold_push;
1411 bridge_agent_hold_v_table.pull = bridge_agent_hold_pull;
1414 static int bridge_agent_hold_deferred_create(void)
1416 RAII_VAR(struct ast_bridge *, holding, ao2_global_obj_ref(agent_holding), ao2_cleanup);
1419 ast_mutex_lock(&agent_holding_lock);
1420 holding = ao2_global_obj_ref(agent_holding);
1422 holding = bridge_agent_hold_new();
1423 ao2_global_obj_replace_unref(agent_holding, holding);
1425 ast_mutex_unlock(&agent_holding_lock);
1427 ast_log(LOG_ERROR, "Could not create agent holding bridge.\n");
1434 static void send_agent_login(struct ast_channel *chan, const char *agent)
1436 RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
1438 ast_assert(agent != NULL);
1440 blob = ast_json_pack("{s: s}",
1446 ast_channel_publish_cached_blob(chan, ast_channel_agent_login_type(), blob);
1449 static void send_agent_logoff(struct ast_channel *chan, const char *agent, long logintime)
1451 RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
1453 ast_assert(agent != NULL);
1455 blob = ast_json_pack("{s: s, s: i}",
1457 "logintime", logintime);
1462 ast_channel_publish_cached_blob(chan, ast_channel_agent_logoff_type(), blob);
1467 * \brief Logout the agent.
1470 * \param agent Which agent logging out.
1472 * \note On entry agent is already locked. On exit it is no longer locked.
1476 static void agent_logout(struct agent_pvt *agent)
1478 struct ast_channel *logged;
1479 struct ast_bridge *caller_bridge;
1480 long time_logged_in;
1482 time_logged_in = time(NULL) - agent->login_start;
1483 logged = agent->logged;
1484 agent->logged = NULL;
1485 caller_bridge = agent->caller_bridge;
1486 agent->caller_bridge = NULL;
1487 agent->state = AGENT_STATE_LOGGED_OUT;
1488 agent->devstate = AST_DEVICE_UNAVAILABLE;
1489 ast_clear_flag(agent, AST_FLAGS_ALL);
1490 agent_unlock(agent);
1491 agent_devstate_changed(agent->username);
1493 if (caller_bridge) {
1494 ast_bridge_destroy(caller_bridge, 0);
1497 ast_channel_lock(logged);
1498 send_agent_logoff(logged, agent->username, time_logged_in);
1499 ast_channel_unlock(logged);
1500 ast_verb(2, "Agent '%s' logged out. Logged in for %ld seconds.\n",
1501 agent->username, time_logged_in);
1502 ast_channel_unref(logged);
1507 * \brief Agent driver loop.
1510 * \param agent Which agent.
1511 * \param logged The logged in channel.
1515 static void agent_run(struct agent_pvt *agent, struct ast_channel *logged)
1517 struct ast_bridge_features features;
1519 if (ast_bridge_features_init(&features)) {
1520 ast_channel_hangupcause_set(logged, AST_CAUSE_NORMAL_CLEARING);
1521 goto agent_run_cleanup;
1524 struct agents_cfg *cfgs;
1525 struct agent_cfg *cfg_new;
1526 struct agent_cfg *cfg_old;
1527 struct ast_bridge *holding;
1528 struct ast_bridge *caller_bridge;
1530 ast_channel_hangupcause_set(logged, AST_CAUSE_NORMAL_CLEARING);
1532 holding = ao2_global_obj_ref(agent_holding);
1534 ast_debug(1, "Agent %s: Someone destroyed the agent holding bridge.\n",
1540 * When the agent channel leaves the bridging system we usually
1541 * want to put the agent back into the holding bridge for the
1544 ast_bridge_join(holding, logged, NULL, &features, NULL,
1545 AST_BRIDGE_JOIN_PASS_REFERENCE);
1546 if (logged != agent->logged) {
1547 /* This channel is no longer the logged in agent. */
1552 /* The agent is no longer configured. */
1556 /* Update the agent's config before rejoining the holding bridge. */
1557 cfgs = ao2_global_obj_ref(cfg_handle);
1559 /* There is no agent configuration. All agents were destroyed. */
1562 cfg_new = ao2_find(cfgs->agents, agent->username, OBJ_KEY);
1565 /* The agent is no longer configured. */
1569 cfg_old = agent->cfg;
1570 agent->cfg = cfg_new;
1572 agent->last_disconnect = ast_tvnow();
1574 /* Clear out any caller bridge before rejoining the holding bridge. */
1575 caller_bridge = agent->caller_bridge;
1576 agent->caller_bridge = NULL;
1577 agent_unlock(agent);
1578 ao2_ref(cfg_old, -1);
1579 if (caller_bridge) {
1580 ast_bridge_destroy(caller_bridge, 0);
1583 if (agent->state == AGENT_STATE_LOGGING_OUT
1584 || agent->deferred_logoff
1585 || ast_check_hangup_locked(logged)) {
1586 /* The agent was requested to logout or hungup. */
1591 * It is safe to access agent->waiting_colp without a lock. It
1592 * is only setup on agent login and not changed.
1594 ast_channel_update_connected_line(logged, &agent->waiting_colp, NULL);
1596 ast_bridge_features_cleanup(&features);
1600 if (logged != agent->logged) {
1602 * We are no longer the agent channel because of local channel
1605 agent_unlock(agent);
1606 ast_debug(1, "Agent %s: Channel %s is no longer the agent.\n",
1607 agent->username, ast_channel_name(logged));
1610 agent_logout(agent);
1613 static void agent_after_bridge_cb(struct ast_channel *chan, void *data)
1615 struct agent_pvt *agent;
1617 agent = ao2_find(agents, chan, 0);
1622 ast_debug(1, "Agent %s: New agent channel %s.\n",
1623 agent->username, ast_channel_name(chan));
1624 agent_run(agent, chan);
1628 static void agent_after_bridge_cb_failed(enum ast_bridge_after_cb_reason reason, void *data)
1630 struct ast_channel *chan = data;
1631 struct agent_pvt *agent;
1633 agent = ao2_find(agents, chan, 0);
1637 ast_log(LOG_WARNING, "Agent %s: Forced logout. Lost control of %s because: %s\n",
1638 agent->username, ast_channel_name(chan),
1639 ast_bridge_after_cb_reason_string(reason));
1641 agent_logout(agent);
1647 * \brief Get the lock on the agent bridge_channel and return it.
1650 * \param agent Whose bridge_channel to get.
1652 * \retval bridge_channel on success (Reffed and locked).
1653 * \retval NULL on error.
1655 static struct ast_bridge_channel *agent_bridge_channel_get_lock(struct agent_pvt *agent)
1657 struct ast_channel *logged;
1658 struct ast_bridge_channel *bc;
1662 logged = agent->logged;
1664 agent_unlock(agent);
1667 ast_channel_ref(logged);
1668 agent_unlock(agent);
1670 ast_channel_lock(logged);
1671 bc = ast_channel_get_bridge_channel(logged);
1672 ast_channel_unlock(logged);
1673 ast_channel_unref(logged);
1675 if (agent->logged != logged) {
1681 ast_bridge_channel_lock(bc);
1682 if (bc->chan != logged || agent->logged != logged) {
1683 ast_bridge_channel_unlock(bc);
1691 static void caller_abort_agent(struct agent_pvt *agent)
1693 struct ast_bridge_channel *logged;
1695 logged = agent_bridge_channel_get_lock(agent);
1697 struct ast_bridge *caller_bridge;
1699 ast_debug(1, "Agent '%s' no longer logged in.\n", agent->username);
1702 caller_bridge = agent->caller_bridge;
1703 agent->caller_bridge = NULL;
1704 agent_unlock(agent);
1705 if (caller_bridge) {
1706 ast_bridge_destroy(caller_bridge, 0);
1711 /* Kick the agent out of the holding bridge to reset it. */
1712 ast_bridge_channel_leave_bridge_nolock(logged, BRIDGE_CHANNEL_STATE_END,
1713 AST_CAUSE_NORMAL_CLEARING);
1714 ast_bridge_channel_unlock(logged);
1717 static int caller_safety_timeout(struct ast_bridge_channel *bridge_channel, void *hook_pvt)
1719 struct agent_pvt *agent = hook_pvt;
1721 if (agent->state == AGENT_STATE_CALL_PRESENT) {
1722 ast_log(LOG_WARNING, "Agent '%s' process did not respond. Safety timeout.\n",
1724 pbx_builtin_setvar_helper(bridge_channel->chan, "AGENT_STATUS", "ERROR");
1726 ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END, 0);
1727 caller_abort_agent(agent);
1733 static void agent_alert(struct ast_bridge_channel *bridge_channel, const void *payload, size_t payload_size)
1735 const char *agent_id = payload;
1736 const char *playfile;
1737 const char *dtmf_accept;
1738 struct agent_pvt *agent;
1742 agent = ao2_find(agents, agent_id, OBJ_KEY);
1744 ast_debug(1, "Agent '%s' does not exist. Where did it go?\n", agent_id);
1748 /* Change holding bridge participant role's idle mode to silence */
1749 ast_bridge_channel_lock_bridge(bridge_channel);
1750 ast_bridge_channel_clear_roles(bridge_channel);
1751 ast_channel_set_bridge_role_option(bridge_channel->chan, "holding_participant", "idle_mode", "silence");
1752 ast_bridge_channel_establish_roles(bridge_channel);
1753 ast_bridge_unlock(bridge_channel->bridge);
1756 playfile = ast_strdupa(agent->cfg->beep_sound);
1758 /* Determine which DTMF digits interrupt the alerting signal. */
1759 if (ast_test_flag(agent, AGENT_FLAG_ACK_CALL)
1760 ? agent->override_ack_call : agent->cfg->ack_call) {
1761 dtmf_accept = ast_test_flag(agent, AGENT_FLAG_DTMF_ACCEPT)
1762 ? agent->override_dtmf_accept : agent->cfg->dtmf_accept;
1764 /* Only the first digit of the ack will stop playback. */
1765 dtmf[0] = *dtmf_accept;
1771 agent_unlock(agent);
1773 /* Alert the agent. */
1774 digit = ast_stream_and_wait(bridge_channel->chan, playfile,
1775 ast_strlen_zero(dtmf_accept) ? AST_DIGIT_ANY : dtmf_accept);
1776 ast_stopstream(bridge_channel->chan);
1779 switch (agent->state) {
1780 case AGENT_STATE_CALL_PRESENT:
1781 if (!ast_strlen_zero(dtmf_accept)) {
1782 agent->state = AGENT_STATE_CALL_WAIT_ACK;
1783 agent->ack_time = ast_tvnow();
1786 /* Playback was interrupted by a digit. */
1787 agent_unlock(agent);
1789 ast_bridge_channel_feature_digit(bridge_channel, digit);
1795 /* Connect to caller now. */
1796 ast_debug(1, "Agent %s: Immediately connecting to call.\n", agent->username);
1797 agent_connect_caller(bridge_channel, agent);/* Will unlock agent. */
1803 agent_unlock(agent);
1807 static int send_alert_to_agent(struct ast_bridge_channel *bridge_channel, const char *agent_id)
1809 return ast_bridge_channel_queue_callback(bridge_channel,
1810 AST_BRIDGE_CHANNEL_CB_OPTION_MEDIA, agent_alert, agent_id, strlen(agent_id) + 1);
1813 static int send_colp_to_agent(struct ast_bridge_channel *bridge_channel, struct ast_party_connected_line *connected)
1815 struct ast_set_party_connected_line update = {
1820 unsigned char data[1024]; /* This should be large enough */
1823 datalen = ast_connected_line_build_data(data, sizeof(data), connected, &update);
1824 if (datalen == (size_t) -1) {
1828 return ast_bridge_channel_queue_control_data(bridge_channel,
1829 AST_CONTROL_CONNECTED_LINE, data, datalen);
1834 * \brief Caller joined the bridge event callback.
1836 * \param bridge_channel Channel executing the feature
1837 * \param hook_pvt Private data passed in when the hook was created
1839 * \retval 0 Keep the callback hook.
1840 * \retval -1 Remove the callback hook.
1842 static int caller_joined_bridge(struct ast_bridge_channel *bridge_channel, void *hook_pvt)
1844 struct agent_pvt *agent = hook_pvt;
1845 struct ast_bridge_channel *logged;
1848 logged = agent_bridge_channel_get_lock(agent);
1850 ast_verb(3, "Agent '%s' not logged in.\n", agent->username);
1851 pbx_builtin_setvar_helper(bridge_channel->chan, "AGENT_STATUS", "NOT_LOGGED_IN");
1853 ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END, 0);
1854 caller_abort_agent(agent);
1858 res = send_alert_to_agent(logged, agent->username);
1859 ast_bridge_channel_unlock(logged);
1860 ao2_ref(logged, -1);
1862 ast_verb(3, "Agent '%s': Failed to alert the agent.\n", agent->username);
1863 pbx_builtin_setvar_helper(bridge_channel->chan, "AGENT_STATUS", "ERROR");
1865 ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END, 0);
1866 caller_abort_agent(agent);
1870 pbx_builtin_setvar_helper(bridge_channel->chan, "AGENT_STATUS", "NOT_CONNECTED");
1871 ast_indicate(bridge_channel->chan, AST_CONTROL_RINGING);
1876 * \brief Dialplan AgentRequest application to locate an agent to talk with.
1878 * \param chan Channel wanting to talk with an agent.
1879 * \param data Application parameters
1881 * \retval 0 To continue in dialplan.
1882 * \retval -1 To hangup.
1884 static int agent_request_exec(struct ast_channel *chan, const char *data)
1886 struct ast_bridge *caller_bridge;
1887 struct ast_bridge_channel *logged;
1890 struct ast_bridge_features caller_features;
1891 struct ast_party_connected_line connected;
1892 AST_DECLARE_APP_ARGS(args,
1893 AST_APP_ARG(agent_id);
1894 AST_APP_ARG(other); /* Any remaining unused arguments */
1897 RAII_VAR(struct agent_pvt *, agent, NULL, ao2_cleanup);
1899 if (bridge_agent_hold_deferred_create()) {
1903 parse = ast_strdupa(data);
1904 AST_STANDARD_APP_ARGS(args, parse);
1906 if (ast_strlen_zero(args.agent_id)) {
1907 ast_log(LOG_WARNING, "AgentRequest requires an AgentId\n");
1911 /* Find the agent. */
1912 agent = ao2_find(agents, args.agent_id, OBJ_KEY);
1914 ast_verb(3, "Agent '%s' does not exist.\n", args.agent_id);
1915 pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "INVALID");
1919 if (ast_bridge_features_init(&caller_features)) {
1923 /* Add safety timeout hook. */
1925 if (ast_bridge_interval_hook(&caller_features, 0, CALLER_SAFETY_TIMEOUT_TIME,
1926 caller_safety_timeout, agent, __ao2_cleanup, AST_BRIDGE_HOOK_REMOVE_ON_PULL)) {
1928 ast_bridge_features_cleanup(&caller_features);
1932 /* Setup the alert agent on caller joining the bridge hook. */
1934 if (ast_bridge_join_hook(&caller_features, caller_joined_bridge, agent,
1935 __ao2_cleanup, 0)) {
1937 ast_bridge_features_cleanup(&caller_features);
1941 caller_bridge = ast_bridge_basic_new();
1942 if (!caller_bridge) {
1943 ast_bridge_features_cleanup(&caller_features);
1948 switch (agent->state) {
1949 case AGENT_STATE_LOGGED_OUT:
1950 case AGENT_STATE_LOGGING_OUT:
1951 agent_unlock(agent);
1952 ast_bridge_destroy(caller_bridge, 0);
1953 ast_bridge_features_cleanup(&caller_features);
1954 ast_verb(3, "Agent '%s' not logged in.\n", agent->username);
1955 pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "NOT_LOGGED_IN");
1957 case AGENT_STATE_READY_FOR_CALL:
1958 ao2_ref(caller_bridge, +1);
1959 agent->caller_bridge = caller_bridge;
1960 agent->state = AGENT_STATE_CALL_PRESENT;
1961 agent->devstate = AST_DEVICE_INUSE;
1964 agent_unlock(agent);
1965 ast_bridge_destroy(caller_bridge, 0);
1966 ast_bridge_features_cleanup(&caller_features);
1967 ast_verb(3, "Agent '%s' is busy.\n", agent->username);
1968 pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "BUSY");
1971 agent_unlock(agent);
1972 agent_devstate_changed(agent->username);
1974 /* Get COLP for agent. */
1975 ast_party_connected_line_init(&connected);
1976 ast_channel_lock(chan);
1977 ast_connected_line_copy_from_caller(&connected, ast_channel_caller(chan));
1978 ast_channel_unlock(chan);
1980 logged = agent_bridge_channel_get_lock(agent);
1982 ast_party_connected_line_free(&connected);
1983 caller_abort_agent(agent);
1984 ast_bridge_destroy(caller_bridge, 0);
1985 ast_bridge_features_cleanup(&caller_features);
1986 ast_verb(3, "Agent '%s' not logged in.\n", agent->username);
1987 pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "NOT_LOGGED_IN");
1991 send_colp_to_agent(logged, &connected);
1992 ast_bridge_channel_unlock(logged);
1993 ao2_ref(logged, -1);
1994 ast_party_connected_line_free(&connected);
1996 if (ast_bridge_join(caller_bridge, chan, NULL, &caller_features, NULL,
1997 AST_BRIDGE_JOIN_PASS_REFERENCE)) {
1998 caller_abort_agent(agent);
1999 ast_verb(3, "Agent '%s': Caller %s failed to join the bridge.\n",
2000 agent->username, ast_channel_name(chan));
2001 pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "ERROR");
2003 ast_bridge_features_cleanup(&caller_features);
2005 /* Determine if we need to continue in the dialplan after the bridge. */
2006 ast_channel_lock(chan);
2007 if (ast_channel_softhangup_internal_flag(chan) & AST_SOFTHANGUP_ASYNCGOTO) {
2009 * The bridge was broken for a hangup that isn't real.
2010 * Don't run the h extension, because the channel isn't
2011 * really hung up. This should really only happen with
2012 * AST_SOFTHANGUP_ASYNCGOTO.
2016 res = ast_check_hangup(chan)
2017 || ast_test_flag(ast_channel_flags(chan), AST_FLAG_ZOMBIE)
2018 || ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENT_STATUS"));
2020 ast_channel_unlock(chan);
2022 return res ? -1 : 0;
2027 * \brief Get agent config values from the login channel.
2030 * \param agent What to setup channel config values on.
2031 * \param chan Channel logging in as an agent.
2035 static void agent_login_channel_config(struct agent_pvt *agent, struct ast_channel *chan)
2037 struct ast_flags opts = { 0 };
2038 struct ast_party_connected_line connected;
2039 unsigned int override_ack_call = 0;
2040 unsigned int override_auto_logoff = 0;
2041 unsigned int override_wrapup_time = 0;
2042 const char *override_dtmf_accept = NULL;
2045 ast_party_connected_line_init(&connected);
2047 /* Get config values from channel. */
2048 ast_channel_lock(chan);
2049 ast_party_connected_line_copy(&connected, ast_channel_connected(chan));
2051 var = pbx_builtin_getvar_helper(chan, "AGENTACKCALL");
2052 if (!ast_strlen_zero(var)) {
2053 override_ack_call = ast_true(var) ? 1 : 0;
2054 ast_set_flag(&opts, AGENT_FLAG_ACK_CALL);
2057 var = pbx_builtin_getvar_helper(chan, "AGENTACCEPTDTMF");
2058 if (!ast_strlen_zero(var)) {
2059 override_dtmf_accept = ast_strdupa(var);
2060 ast_set_flag(&opts, AGENT_FLAG_DTMF_ACCEPT);
2063 var = pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF");
2064 if (!ast_strlen_zero(var)) {
2065 if (sscanf(var, "%u", &override_auto_logoff) == 1) {
2066 ast_set_flag(&opts, AGENT_FLAG_AUTO_LOGOFF);
2070 var = pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME");
2071 if (!ast_strlen_zero(var)) {
2072 if (sscanf(var, "%u", &override_wrapup_time) == 1) {
2073 ast_set_flag(&opts, AGENT_FLAG_WRAPUP_TIME);
2076 ast_channel_unlock(chan);
2078 /* Set config values on agent. */
2080 ast_party_connected_line_free(&agent->waiting_colp);
2081 agent->waiting_colp = connected;
2083 ast_string_field_set(agent, override_dtmf_accept, override_dtmf_accept);
2084 ast_copy_flags(agent, &opts, AST_FLAGS_ALL);
2085 agent->override_auto_logoff = override_auto_logoff;
2086 agent->override_wrapup_time = override_wrapup_time;
2087 agent->override_ack_call = override_ack_call;
2088 agent_unlock(agent);
2091 enum AGENT_LOGIN_OPT_FLAGS {
2092 OPT_SILENT = (1 << 0),
2094 AST_APP_OPTIONS(agent_login_opts, BEGIN_OPTIONS
2095 AST_APP_OPTION('s', OPT_SILENT),
2099 * \brief Dialplan AgentLogin application to log in an agent.
2101 * \param chan Channel attempting to login as an agent.
2102 * \param data Application parameters
2104 * \retval 0 To continue in dialplan.
2105 * \retval -1 To hangup.
2107 static int agent_login_exec(struct ast_channel *chan, const char *data)
2110 struct ast_flags opts;
2111 AST_DECLARE_APP_ARGS(args,
2112 AST_APP_ARG(agent_id);
2113 AST_APP_ARG(options);
2114 AST_APP_ARG(other); /* Any remaining unused arguments */
2117 RAII_VAR(struct agent_pvt *, agent, NULL, ao2_cleanup);
2119 if (bridge_agent_hold_deferred_create()) {
2123 if (ast_channel_state(chan) != AST_STATE_UP && ast_answer(chan)) {
2127 parse = ast_strdupa(data);
2128 AST_STANDARD_APP_ARGS(args, parse);
2130 if (ast_strlen_zero(args.agent_id)) {
2131 ast_log(LOG_WARNING, "AgentLogin requires an AgentId\n");
2135 if (ast_app_parse_options(agent_login_opts, &opts, NULL, args.options)) {
2136 /* General invalid option syntax. */
2140 /* Find the agent. */
2141 agent = ao2_find(agents, args.agent_id, OBJ_KEY);
2143 ast_verb(3, "Agent '%s' does not exist.\n", args.agent_id);
2144 pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "INVALID");
2148 /* Has someone already logged in as this agent already? */
2150 if (agent->logged) {
2151 agent_unlock(agent);
2152 ast_verb(3, "Agent '%s' already logged in.\n", agent->username);
2153 pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "ALREADY_LOGGED_IN");
2156 agent->logged = ast_channel_ref(chan);
2157 agent->last_disconnect = ast_tvnow();
2158 time(&agent->login_start);
2159 agent->deferred_logoff = 0;
2160 agent_unlock(agent);
2162 agent_login_channel_config(agent, chan);
2164 if (!ast_test_flag(&opts, OPT_SILENT)) {
2165 ast_stream_and_wait(chan, "agent-loginok", AST_DIGIT_NONE);
2168 ast_verb(2, "Agent '%s' logged in (format %s/%s)\n", agent->username,
2169 ast_format_get_name(ast_channel_readformat(chan)),
2170 ast_format_get_name(ast_channel_writeformat(chan)));
2171 ast_channel_lock(chan);
2172 send_agent_login(chan, agent->username);
2173 ast_channel_unlock(chan);
2175 agent_run(agent, chan);
2179 static int agent_function_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
2182 struct agent_pvt *agent;
2183 struct ast_channel *logged;
2184 AST_DECLARE_APP_ARGS(args,
2185 AST_APP_ARG(agentid);
2191 parse = ast_strdupa(data ?: "");
2192 AST_NONSTANDARD_APP_ARGS(args, parse, ':');
2194 if (ast_strlen_zero(args.agentid)) {
2195 ast_log(LOG_WARNING, "The AGENT function requires an argument - agentid!\n");
2199 args.item = "status";
2202 agent = ao2_find(agents, args.agentid, OBJ_KEY);
2204 ast_log(LOG_WARNING, "Agent '%s' not found!\n", args.agentid);
2209 if (!strcasecmp(args.item, "status")) {
2212 if (agent->logged) {
2213 status = "LOGGEDIN";
2215 status = "LOGGEDOUT";
2217 ast_copy_string(buf, status, len);
2218 } else if (!strcasecmp(args.item, "name")) {
2219 ast_copy_string(buf, agent->cfg->full_name, len);
2220 } else if (!strcasecmp(args.item, "mohclass")) {
2221 ast_copy_string(buf, agent->cfg->moh, len);
2222 } else if (!strcasecmp(args.item, "channel")) {
2223 logged = agent_lock_logged(agent);
2227 ast_copy_string(buf, ast_channel_name(logged), len);
2228 ast_channel_unlock(logged);
2229 ast_channel_unref(logged);
2231 pos = strrchr(buf, '-');
2236 } else if (!strcasecmp(args.item, "fullchannel")) {
2237 logged = agent_lock_logged(agent);
2239 ast_copy_string(buf, ast_channel_name(logged), len);
2240 ast_channel_unlock(logged);
2241 ast_channel_unref(logged);
2244 agent_unlock(agent);
2250 static struct ast_custom_function agent_function = {
2252 .read = agent_function_read,
2255 struct agent_complete {
2256 /*! Nth match to return. */
2258 /*! Which match currently on. */
2262 static int complete_agent_search(void *obj, void *arg, void *data, int flags)
2264 struct agent_complete *search = data;
2266 if (++search->which > search->state) {
2272 static char *complete_agent(const char *word, int state)
2275 struct agent_pvt *agent;
2276 struct agent_complete search = {
2280 agent = ao2_callback_data(agents, ast_strlen_zero(word) ? 0 : OBJ_PARTIAL_KEY,
2281 complete_agent_search, (char *) word, &search);
2285 ret = ast_strdup(agent->username);
2290 static int complete_agent_logoff_search(void *obj, void *arg, void *data, int flags)
2292 struct agent_pvt *agent = obj;
2293 struct agent_complete *search = data;
2295 if (!agent->logged) {
2298 if (++search->which > search->state) {
2304 static char *complete_agent_logoff(const char *word, int state)
2307 struct agent_pvt *agent;
2308 struct agent_complete search = {
2312 agent = ao2_callback_data(agents, ast_strlen_zero(word) ? 0 : OBJ_PARTIAL_KEY,
2313 complete_agent_logoff_search, (char *) word, &search);
2317 ret = ast_strdup(agent->username);
2322 static void agent_show_requested(struct ast_cli_args *a, int online_only)
2324 #define FORMAT_HDR "%-8s %-20s %-11s %-30s %s\n"
2325 #define FORMAT_ROW "%-8s %-20s %-11s %-30s %s\n"
2327 struct ao2_iterator iter;
2328 struct agent_pvt *agent;
2329 struct ast_str *out = ast_str_alloca(512);
2330 unsigned int agents_total = 0;
2331 unsigned int agents_logged_in = 0;
2332 unsigned int agents_talking = 0;
2334 ast_cli(a->fd, FORMAT_HDR, "Agent-ID", "Name", "State", "Channel", "Talking with");
2335 iter = ao2_iterator_init(agents, 0);
2336 for (; (agent = ao2_iterator_next(&iter)); ao2_ref(agent, -1)) {
2337 struct ast_channel *logged;
2342 logged = agent_lock_logged(agent);
2344 const char *talking_with;
2348 talking_with = pbx_builtin_getvar_helper(logged, "BRIDGEPEER");
2349 if (!ast_strlen_zero(talking_with)) {
2354 ast_str_set(&out, 0, FORMAT_ROW, agent->username, agent->cfg->full_name,
2355 ast_devstate_str(agent->devstate), ast_channel_name(logged), talking_with);
2356 ast_channel_unlock(logged);
2357 ast_channel_unref(logged);
2359 ast_str_set(&out, 0, FORMAT_ROW, agent->username, agent->cfg->full_name,
2360 ast_devstate_str(agent->devstate), "", "");
2362 agent_unlock(agent);
2364 if (!online_only || logged) {
2365 ast_cli(a->fd, "%s", ast_str_buffer(out));
2368 ao2_iterator_destroy(&iter);
2370 ast_cli(a->fd, "\nDefined agents: %u, Logged in: %u, Talking: %u\n",
2371 agents_total, agents_logged_in, agents_talking);
2377 static char *agent_handle_show_online(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2381 e->command = "agent show online";
2383 "Usage: agent show online\n"
2384 " Provides summary information for logged in agents.\n";
2391 return CLI_SHOWUSAGE;
2394 agent_show_requested(a, 1);
2399 static char *agent_handle_show_all(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2403 e->command = "agent show all";
2405 "Usage: agent show all\n"
2406 " Provides summary information for all agents.\n";
2413 return CLI_SHOWUSAGE;
2416 agent_show_requested(a, 0);
2421 static char *agent_handle_show_specific(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2423 struct agent_pvt *agent;
2424 struct ast_channel *logged;
2425 struct ast_str *out = ast_str_alloca(4096);
2429 e->command = "agent show";
2431 "Usage: agent show <agent-id>\n"
2432 " Show information about the <agent-id> agent\n";
2436 return complete_agent(a->word, a->n);
2442 return CLI_SHOWUSAGE;
2445 agent = ao2_find(agents, a->argv[2], OBJ_KEY);
2447 ast_cli(a->fd, "Agent '%s' not found\n", a->argv[2]);
2452 logged = agent_lock_logged(agent);
2453 ast_str_set(&out, 0, "Id: %s\n", agent->username);
2454 ast_str_append(&out, 0, "Name: %s\n", agent->cfg->full_name);
2455 ast_str_append(&out, 0, "Beep: %s\n", agent->cfg->beep_sound);
2456 ast_str_append(&out, 0, "MOH: %s\n", agent->cfg->moh);
2457 ast_str_append(&out, 0, "RecordCalls: %s\n", AST_CLI_YESNO(agent->cfg->record_agent_calls));
2458 ast_str_append(&out, 0, "State: %s\n", ast_devstate_str(agent->devstate));
2460 const char *talking_with;
2462 ast_str_append(&out, 0, "LoggedInChannel: %s\n", ast_channel_name(logged));
2463 ast_str_append(&out, 0, "LoggedInTime: %ld\n", (long) agent->login_start);
2464 talking_with = pbx_builtin_getvar_helper(logged, "BRIDGEPEER");
2465 if (!ast_strlen_zero(talking_with)) {
2466 ast_str_append(&out, 0, "TalkingWith: %s\n", talking_with);
2467 ast_str_append(&out, 0, "CallStarted: %ld\n", (long) agent->call_start);
2469 ast_channel_unlock(logged);
2470 ast_channel_unref(logged);
2472 agent_unlock(agent);
2475 ast_cli(a->fd, "%s", ast_str_buffer(out));
2480 static char *agent_handle_logoff_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2484 e->command = "agent logoff";
2486 "Usage: agent logoff <agent-id> [soft]\n"
2487 " Sets an agent as no longer logged in.\n"
2488 " If 'soft' is specified, do not hangup existing calls.\n";
2492 return complete_agent_logoff(a->word, a->n);
2493 } else if (a->pos == 3 && a->n == 0
2494 && (ast_strlen_zero(a->word)
2495 || !strncasecmp("soft", a->word, strlen(a->word)))) {
2496 return ast_strdup("soft");
2501 if (a->argc < 3 || 4 < a->argc) {
2502 return CLI_SHOWUSAGE;
2504 if (a->argc == 4 && strcasecmp(a->argv[3], "soft")) {
2505 return CLI_SHOWUSAGE;
2508 if (!agent_logoff_request(a->argv[2], a->argc == 4)) {
2509 ast_cli(a->fd, "Logging out %s\n", a->argv[2]);
2515 static struct ast_cli_entry cli_agents[] = {
2516 AST_CLI_DEFINE(agent_handle_show_online, "Show status of online agents"),
2517 AST_CLI_DEFINE(agent_handle_show_all, "Show status of all agents"),
2518 AST_CLI_DEFINE(agent_handle_show_specific, "Show information about an agent"),
2519 AST_CLI_DEFINE(agent_handle_logoff_cmd, "Sets an agent offline"),
2522 static int action_agents(struct mansession *s, const struct message *m)
2524 const char *id = astman_get_header(m, "ActionID");
2525 char id_text[AST_MAX_BUF];
2526 struct ao2_iterator iter;
2527 struct agent_pvt *agent;
2528 struct ast_str *out = ast_str_alloca(4096);
2531 if (!ast_strlen_zero(id)) {
2532 snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", id);
2536 astman_send_listack(s, m, "Agents will follow", "start");
2538 iter = ao2_iterator_init(agents, 0);
2539 for (; (agent = ao2_iterator_next(&iter)); ao2_ref(agent, -1)) {
2540 struct ast_channel *logged;
2543 logged = agent_lock_logged(agent);
2547 * AGENT_LOGGEDOFF - Agent isn't logged in
2548 * AGENT_IDLE - Agent is logged in, and waiting for call
2549 * AGENT_ONCALL - Agent is logged in, and on a call
2550 * AGENT_UNKNOWN - Don't know anything about agent. Shouldn't ever get this.
2552 ast_str_set(&out, 0, "Agent: %s\r\n", agent->username);
2553 ast_str_append(&out, 0, "Name: %s\r\n", agent->cfg->full_name);
2556 const char *talking_to_chan;
2557 struct ast_str *logged_headers;
2558 RAII_VAR(struct ast_channel_snapshot *, logged_snapshot, ast_channel_snapshot_create(logged), ao2_cleanup);
2560 if (!logged_snapshot
2561 || !(logged_headers =
2562 ast_manager_build_channel_state_string(logged_snapshot))) {
2563 ast_channel_unlock(logged);
2564 ast_channel_unref(logged);
2565 agent_unlock(agent);
2569 talking_to_chan = pbx_builtin_getvar_helper(logged, "BRIDGEPEER");
2570 if (!ast_strlen_zero(talking_to_chan)) {
2571 ast_str_append(&out, 0, "Status: %s\r\n", "AGENT_ONCALL");
2572 ast_str_append(&out, 0, "TalkingToChan: %s\r\n", talking_to_chan);
2573 ast_str_append(&out, 0, "CallStarted: %ld\n", (long) agent->call_start);
2575 ast_str_append(&out, 0, "Status: %s\r\n", "AGENT_IDLE");
2577 ast_str_append(&out, 0, "LoggedInTime: %ld\r\n", (long) agent->login_start);
2578 ast_str_append(&out, 0, "%s", ast_str_buffer(logged_headers));
2579 ast_channel_unlock(logged);
2580 ast_channel_unref(logged);
2581 ast_free(logged_headers);
2583 ast_str_append(&out, 0, "Status: %s\r\n", "AGENT_LOGGEDOFF");
2586 agent_unlock(agent);
2588 astman_append(s, "Event: Agents\r\n"
2590 ast_str_buffer(out), id_text);
2593 ao2_iterator_destroy(&iter);
2595 astman_send_list_complete_start(s, m, "AgentsComplete", num_agents);
2596 astman_send_list_complete_end(s);
2600 static int action_agent_logoff(struct mansession *s, const struct message *m)
2602 const char *agent = astman_get_header(m, "Agent");
2603 const char *soft_s = astman_get_header(m, "Soft"); /* "true" is don't hangup */
2605 if (ast_strlen_zero(agent)) {
2606 astman_send_error(s, m, "No agent specified");
2610 if (!agent_logoff_request(agent, ast_true(soft_s))) {
2611 astman_send_ack(s, m, "Agent logged out");
2613 astman_send_error(s, m, "No such agent");
2619 static int unload_module(void)
2621 struct ast_bridge *holding;
2623 /* Unregister dialplan applications */
2624 ast_unregister_application(app_agent_login);
2625 ast_unregister_application(app_agent_request);
2627 /* Unregister dialplan functions */
2628 ast_custom_function_unregister(&agent_function);
2630 /* Unregister manager command */
2631 ast_manager_unregister("Agents");
2632 ast_manager_unregister("AgentLogoff");
2634 /* Unregister CLI commands */
2635 ast_cli_unregister_multiple(cli_agents, ARRAY_LEN(cli_agents));
2637 ast_devstate_prov_del("Agent");
2639 /* Destroy agent holding bridge. */
2640 holding = ao2_global_obj_replace(agent_holding, NULL);
2642 ast_bridge_destroy(holding, 0);
2646 ao2_cleanup(agents);
2651 static int load_module(void)
2655 agents = ao2_container_alloc_rbtree(AO2_ALLOC_OPT_LOCK_MUTEX,
2656 AO2_CONTAINER_ALLOC_OPT_DUPS_REPLACE, agent_pvt_sort_cmp, agent_pvt_cmp);
2658 return AST_MODULE_LOAD_FAILURE;
2661 /* Init agent holding bridge v_table. */
2662 bridge_init_agent_hold();
2664 /* Setup to provide Agent:agent-id device state. */
2665 res |= ast_devstate_prov_add("Agent", agent_pvt_devstate_get);
2668 res |= ast_cli_register_multiple(cli_agents, ARRAY_LEN(cli_agents));
2670 /* Manager commands */
2671 res |= ast_manager_register_xml("Agents", EVENT_FLAG_AGENT, action_agents);
2672 res |= ast_manager_register_xml("AgentLogoff", EVENT_FLAG_AGENT, action_agent_logoff);
2674 /* Dialplan Functions */
2675 res |= ast_custom_function_register(&agent_function);
2677 /* Dialplan applications */
2678 res |= ast_register_application_xml(app_agent_login, agent_login_exec);
2679 res |= ast_register_application_xml(app_agent_request, agent_request_exec);
2683 return AST_MODULE_LOAD_FAILURE;
2686 if (load_config()) {
2687 ast_log(LOG_ERROR, "Unable to load config. Not loading module.\n");
2689 return AST_MODULE_LOAD_DECLINE;
2692 return AST_MODULE_LOAD_SUCCESS;
2695 static int reload(void)
2697 if (aco_process_config(&cfg_info, 1) == ACO_PROCESS_ERROR) {
2698 /* Just keep the config we already have in place. */
2704 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Call center agent pool applications",
2705 .support_level = AST_MODULE_SUPPORT_CORE,
2706 .load = load_module,
2707 .unload = unload_module,
2709 .load_pri = AST_MODPRI_DEVSTATE_PROVIDER,