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) {
566 /*! The agent is defined but an agent is not present. */
567 AGENT_STATE_LOGGED_OUT,
568 /*! Forced initial login wait to allow any local channel optimizations to happen. */
569 AGENT_STATE_PROBATION_WAIT,
570 /*! The agent is ready for a call. */
571 AGENT_STATE_READY_FOR_CALL,
572 /*! The agent has a call waiting to connect. */
573 AGENT_STATE_CALL_PRESENT,
574 /*! The agent needs to ack the call. */
575 AGENT_STATE_CALL_WAIT_ACK,
576 /*! The agent is connected with a call. */
578 /*! The agent is resting between calls. */
579 AGENT_STATE_CALL_WRAPUP,
580 /*! The agent is being kicked out. */
581 AGENT_STATE_LOGGING_OUT,
584 /*! Agent config option override flags. */
585 enum agent_override_flags {
586 AGENT_FLAG_ACK_CALL = (1 << 0),
587 AGENT_FLAG_DTMF_ACCEPT = (1 << 1),
588 AGENT_FLAG_AUTO_LOGOFF = (1 << 2),
589 AGENT_FLAG_WRAPUP_TIME = (1 << 3),
592 /*! \brief Structure representing an agent. */
594 AST_DECLARE_STRING_FIELDS(
595 /*! Identification of the agent. (agents container key) */
596 AST_STRING_FIELD(username);
597 /*! Login override DTMF string for an agent to accept a call. */
598 AST_STRING_FIELD(override_dtmf_accept);
600 /*! Connected line information to send when reentering the holding bridge. */
601 struct ast_party_connected_line waiting_colp;
602 /*! Flags show if settings were overridden by channel vars. */
604 /*! Login override number of seconds for agent to ack a call before being logged off. */
605 unsigned int override_auto_logoff;
606 /*! Login override time after a call in ms before the agent can get a new call. */
607 unsigned int override_wrapup_time;
608 /*! Login override if agent needs to ack a call to accept it. */
609 unsigned int override_ack_call:1;
611 /*! TRUE if the agent is requested to logoff when the current call ends. */
612 unsigned int deferred_logoff:1;
614 /*! Mark and sweep config update to determine if an agent is dead. */
615 unsigned int the_mark:1;
617 * \brief TRUE if the agent is no longer configured and is being destroyed.
619 * \note Agents cannot log in if they are dead.
623 /*! Agent control state variable. */
624 enum agent_state state;
625 /*! Custom device state of agent. */
626 enum ast_device_state devstate;
628 /*! When agent first logged in */
630 /*! When agent login probation started. */
631 time_t probation_start;
632 /*! When call started */
634 /*! When ack timer started */
635 struct timeval ack_time;
636 /*! When last disconnected */
637 struct timeval last_disconnect;
639 /*! Caller is waiting in this bridge for agent to join. (Holds ref) */
640 struct ast_bridge *caller_bridge;
641 /*! Agent is logged in with this channel. (Holds ref) (NULL if not logged in.) */
642 struct ast_channel *logged;
643 /*! Active config values from config file. (Holds ref) */
644 struct agent_cfg *cfg;
647 /*! Container of defined agents. */
648 static struct ao2_container *agents;
651 * \brief Lock the agent.
653 * \param agent Agent to lock
657 #define agent_lock(agent) _agent_lock(agent, __FILE__, __PRETTY_FUNCTION__, __LINE__, #agent)
658 static inline void _agent_lock(struct agent_pvt *agent, const char *file, const char *function, int line, const char *var)
660 __ao2_lock(agent, AO2_LOCK_REQ_MUTEX, file, function, line, var);
664 * \brief Unlock the agent.
666 * \param agent Agent to unlock
670 #define agent_unlock(agent) _agent_unlock(agent, __FILE__, __PRETTY_FUNCTION__, __LINE__, #agent)
671 static inline void _agent_unlock(struct agent_pvt *agent, const char *file, const char *function, int line, const char *var)
673 __ao2_unlock(agent, file, function, line, var);
678 * \brief Obtain the agent logged in channel lock if it exists.
681 * \param agent Pointer to the LOCKED agent_pvt.
683 * \note Assumes the agent lock is already obtained.
685 * \note Defined locking order is channel lock then agent lock.
689 static struct ast_channel *agent_lock_logged(struct agent_pvt *agent)
691 struct ast_channel *logged;
694 if (!agent->logged) { /* No owner. Nothing to do. */
698 /* If we don't ref the logged, it could be killed when we unlock the agent. */
699 logged = ast_channel_ref(agent->logged);
701 /* Locking logged requires us to lock channel, then agent. */
703 ast_channel_lock(logged);
706 /* Check if logged changed during agent unlock period */
707 if (logged != agent->logged) {
708 /* Channel changed. Unref and do another pass. */
709 ast_channel_unlock(logged);
710 ast_channel_unref(logged);
712 /* Channel stayed the same. Return it. */
720 * \brief Get the Agent:agent_id device state.
723 * \param agent_id Username of the agent.
726 * Search the agents container for the agent and return the
729 * \return Device state of the agent.
731 static enum ast_device_state agent_pvt_devstate_get(const char *agent_id)
733 RAII_VAR(struct agent_pvt *, agent, ao2_find(agents, agent_id, OBJ_KEY), ao2_cleanup);
736 return agent->devstate;
738 return AST_DEVICE_INVALID;
743 * \brief Request an agent device state be updated.
746 * \param agent_id Which agent needs the device state updated.
750 static void agent_devstate_changed(const char *agent_id)
752 ast_devstate_changed(AST_DEVICE_UNKNOWN, AST_DEVSTATE_CACHABLE, "Agent:%s", agent_id);
755 static void agent_pvt_destructor(void *vdoomed)
757 struct agent_pvt *doomed = vdoomed;
759 /* Make sure device state reflects agent destruction. */
760 if (!ast_strlen_zero(doomed->username)) {
761 ast_debug(1, "Agent %s: Destroyed.\n", doomed->username);
762 agent_devstate_changed(doomed->username);
765 ast_party_connected_line_free(&doomed->waiting_colp);
766 if (doomed->caller_bridge) {
767 ast_bridge_destroy(doomed->caller_bridge, 0);
768 doomed->caller_bridge = NULL;
770 if (doomed->logged) {
771 doomed->logged = ast_channel_unref(doomed->logged);
773 ao2_cleanup(doomed->cfg);
775 ast_string_field_free_memory(doomed);
778 static struct agent_pvt *agent_pvt_new(struct agent_cfg *cfg)
780 struct agent_pvt *agent;
782 agent = ao2_alloc(sizeof(*agent), agent_pvt_destructor);
786 if (ast_string_field_init(agent, 32)) {
790 ast_string_field_set(agent, username, cfg->username);
791 ast_party_connected_line_init(&agent->waiting_colp);
794 agent->devstate = AST_DEVICE_UNAVAILABLE;
800 * \brief Agents ao2 container sort function.
803 * \param obj_left pointer to the (user-defined part) of an object.
804 * \param obj_right pointer to the (user-defined part) of an object.
805 * \param flags flags from ao2_callback()
806 * OBJ_POINTER - if set, 'obj_right', is an object.
807 * OBJ_KEY - if set, 'obj_right', is a search key item that is not an object.
808 * OBJ_PARTIAL_KEY - if set, 'obj_right', is a partial search key item that is not an object.
810 * \retval <0 if obj_left < obj_right
811 * \retval =0 if obj_left == obj_right
812 * \retval >0 if obj_left > obj_right
814 static int agent_pvt_sort_cmp(const void *obj_left, const void *obj_right, int flags)
816 const struct agent_pvt *agent_left = obj_left;
817 const struct agent_pvt *agent_right = obj_right;
818 const char *right_key = obj_right;
821 switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
824 right_key = agent_right->username;
827 cmp = strcmp(agent_left->username, right_key);
829 case OBJ_PARTIAL_KEY:
830 cmp = strncmp(agent_left->username, right_key, strlen(right_key));
838 * \brief ao2_find() callback function.
842 * found = ao2_find(agents, agent, OBJ_POINTER);
843 * found = ao2_find(agents, "agent-id", OBJ_KEY);
844 * found = ao2_find(agents, agent->logged, 0);
846 static int agent_pvt_cmp(void *obj, void *arg, int flags)
848 const struct agent_pvt *agent = obj;
851 switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
854 case OBJ_PARTIAL_KEY:
858 if (agent->logged == arg) {
868 static int agent_mark(void *obj, void *arg, int flags)
870 struct agent_pvt *agent = obj;
878 static void agents_mark(void)
880 ao2_callback(agents, 0, agent_mark, NULL);
883 static int agent_sweep(void *obj, void *arg, int flags)
885 struct agent_pvt *agent = obj;
889 if (agent->the_mark) {
892 /* Unlink dead agents immediately. */
899 static void agents_sweep(void)
901 struct ao2_iterator *iter;
902 struct agent_pvt *agent;
903 struct ast_channel *logged;
905 iter = ao2_callback(agents, OBJ_MULTIPLE | OBJ_UNLINK, agent_sweep, NULL);
909 for (; (agent = ao2_iterator_next(iter)); ao2_ref(agent, -1)) {
912 logged = ast_channel_ref(agent->logged);
921 "Forced logoff of agent %s(%s). Agent no longer configured.\n",
922 agent->username, ast_channel_name(logged));
923 ast_softhangup(logged, AST_SOFTHANGUP_EXPLICIT);
924 ast_channel_unref(logged);
926 ao2_iterator_destroy(iter);
929 static void agents_post_apply_config(void)
931 struct ao2_iterator iter;
932 struct agent_cfg *cfg;
933 RAII_VAR(struct agents_cfg *, cfgs, ao2_global_obj_ref(cfg_handle), ao2_cleanup);
935 ast_assert(cfgs != NULL);
938 iter = ao2_iterator_init(cfgs->agents, 0);
939 for (; (cfg = ao2_iterator_next(&iter)); ao2_ref(cfg, -1)) {
940 RAII_VAR(struct agent_pvt *, agent, ao2_find(agents, cfg->username, OBJ_KEY), ao2_cleanup);
945 if (!agent->logged) {
946 struct agent_cfg *cfg_old;
948 /* Replace the config of agents not logged in. */
949 cfg_old = agent->cfg;
952 ao2_cleanup(cfg_old);
957 agent = agent_pvt_new(cfg);
961 ao2_link(agents, agent);
962 ast_debug(1, "Agent %s: Created.\n", agent->username);
963 agent_devstate_changed(agent->username);
965 ao2_iterator_destroy(&iter);
969 static int agent_logoff_request(const char *agent_id, int soft)
971 struct ast_channel *logged;
972 RAII_VAR(struct agent_pvt *, agent, ao2_find(agents, agent_id, OBJ_KEY), ao2_cleanup);
979 logged = agent_lock_logged(agent);
982 agent->deferred_logoff = 1;
984 ast_softhangup(logged, AST_SOFTHANGUP_EXPLICIT);
986 ast_channel_unlock(logged);
987 ast_channel_unref(logged);
993 /*! Agent holding bridge instance holder. */
994 static AO2_GLOBAL_OBJ_STATIC(agent_holding);
996 /*! Agent holding bridge deferred creation lock. */
997 AST_MUTEX_DEFINE_STATIC(agent_holding_lock);
1001 * \brief Callback to clear AGENT_STATUS on the caller channel.
1003 * \param bridge_channel Which channel to operate on.
1004 * \param payload Data to pass to the callback. (NULL if none).
1005 * \param payload_size Size of the payload if payload is non-NULL. A number otherwise.
1007 * \note The payload MUST NOT have any resources that need to be freed.
1011 static void clear_agent_status(struct ast_bridge_channel *bridge_channel, const void *payload, size_t payload_size)
1013 pbx_builtin_setvar_helper(bridge_channel->chan, "AGENT_STATUS", NULL);
1018 * \brief Connect the agent with the waiting caller.
1021 * \param bridge_channel Agent channel connecting to the caller.
1022 * \param agent Which agent is connecting to the caller.
1024 * \note The agent is locked on entry and not locked on exit.
1028 static void agent_connect_caller(struct ast_bridge_channel *bridge_channel, struct agent_pvt *agent)
1030 struct ast_bridge *caller_bridge;
1031 int record_agent_calls;
1034 record_agent_calls = agent->cfg->record_agent_calls;
1035 caller_bridge = agent->caller_bridge;
1036 agent->caller_bridge = NULL;
1037 agent->state = AGENT_STATE_ON_CALL;
1038 time(&agent->call_start);
1039 agent_unlock(agent);
1041 if (!caller_bridge) {
1043 ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END,
1044 AST_CAUSE_NORMAL_CLEARING);
1047 res = ast_bridge_move(caller_bridge, bridge_channel->bridge, bridge_channel->chan,
1051 ast_bridge_destroy(caller_bridge, 0);
1052 ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END,
1053 AST_CAUSE_NORMAL_CLEARING);
1056 res = ast_bridge_channel_write_control_data(bridge_channel, AST_CONTROL_ANSWER, NULL, 0)
1057 || ast_bridge_channel_write_callback(bridge_channel, 0, clear_agent_status, NULL, 0);
1060 ast_bridge_destroy(caller_bridge, 0);
1064 if (record_agent_calls) {
1065 struct ast_bridge_features_automixmonitor options = {
1066 .start_stop = AUTO_MONITOR_START,
1070 * The agent is in the new bridge so we can invoke the
1071 * mixmonitor hook to only start recording.
1073 ast_bridge_features_do(AST_BRIDGE_BUILTIN_AUTOMIXMON, bridge_channel, &options);
1076 ao2_t_ref(caller_bridge, -1, "Agent successfully in caller_bridge");
1079 static int bridge_agent_hold_ack(struct ast_bridge_channel *bridge_channel, void *hook_pvt)
1081 struct agent_pvt *agent = hook_pvt;
1084 switch (agent->state) {
1085 case AGENT_STATE_CALL_WAIT_ACK:
1086 /* Connect to caller now. */
1087 ast_debug(1, "Agent %s: Acked call.\n", agent->username);
1088 agent_connect_caller(bridge_channel, agent);/* Will unlock agent. */
1093 agent_unlock(agent);
1097 static int bridge_agent_hold_heartbeat(struct ast_bridge_channel *bridge_channel, void *hook_pvt)
1099 struct agent_pvt *agent = hook_pvt;
1100 int probation_timedout = 0;
1101 int ack_timedout = 0;
1102 int wrapup_timedout = 0;
1103 int deferred_logoff;
1104 unsigned int wrapup_time;
1105 unsigned int auto_logoff;
1108 deferred_logoff = agent->deferred_logoff;
1109 if (deferred_logoff) {
1110 agent->state = AGENT_STATE_LOGGING_OUT;
1113 switch (agent->state) {
1114 case AGENT_STATE_PROBATION_WAIT:
1115 probation_timedout =
1116 LOGIN_WAIT_TIMEOUT_TIME <= (time(NULL) - agent->probation_start);
1117 if (probation_timedout) {
1118 /* Now ready for a caller. */
1119 agent->state = AGENT_STATE_READY_FOR_CALL;
1120 agent->devstate = AST_DEVICE_NOT_INUSE;
1123 case AGENT_STATE_CALL_WAIT_ACK:
1124 /* Check ack call time. */
1125 auto_logoff = agent->cfg->auto_logoff;
1126 if (ast_test_flag(agent, AGENT_FLAG_AUTO_LOGOFF)) {
1127 auto_logoff = agent->override_auto_logoff;
1130 auto_logoff *= 1000;
1131 ack_timedout = ast_tvdiff_ms(ast_tvnow(), agent->ack_time) > auto_logoff;
1133 agent->state = AGENT_STATE_LOGGING_OUT;
1137 case AGENT_STATE_CALL_WRAPUP:
1138 /* Check wrapup time. */
1139 wrapup_time = agent->cfg->wrapup_time;
1140 if (ast_test_flag(agent, AGENT_FLAG_WRAPUP_TIME)) {
1141 wrapup_time = agent->override_wrapup_time;
1143 wrapup_timedout = ast_tvdiff_ms(ast_tvnow(), agent->last_disconnect) > wrapup_time;
1144 if (wrapup_timedout) {
1145 agent->state = AGENT_STATE_READY_FOR_CALL;
1146 agent->devstate = AST_DEVICE_NOT_INUSE;
1152 agent_unlock(agent);
1154 if (deferred_logoff) {
1155 ast_debug(1, "Agent %s: Deferred logoff.\n", agent->username);
1156 ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END,
1157 AST_CAUSE_NORMAL_CLEARING);
1158 } else if (probation_timedout) {
1159 ast_debug(1, "Agent %s: Login complete.\n", agent->username);
1160 agent_devstate_changed(agent->username);
1161 } else if (ack_timedout) {
1162 ast_debug(1, "Agent %s: Ack call timeout.\n", agent->username);
1163 ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END,
1164 AST_CAUSE_NORMAL_CLEARING);
1165 } else if (wrapup_timedout) {
1166 ast_debug(1, "Agent %s: Wrapup timeout. Ready for new call.\n", agent->username);
1167 agent_devstate_changed(agent->username);
1173 static void agent_after_bridge_cb(struct ast_channel *chan, void *data);
1174 static void agent_after_bridge_cb_failed(enum ast_bridge_after_cb_reason reason, void *data);
1178 * \brief ast_bridge agent_hold push method.
1181 * \param self Bridge to operate upon.
1182 * \param bridge_channel Bridge channel to push.
1183 * \param swap Bridge channel to swap places with if not NULL.
1185 * \note On entry, self is already locked.
1187 * \retval 0 on success
1188 * \retval -1 on failure
1190 static int bridge_agent_hold_push(struct ast_bridge *self, struct ast_bridge_channel *bridge_channel, struct ast_bridge_channel *swap)
1193 unsigned int wrapup_time;
1194 char dtmf[AST_FEATURE_MAX_LEN];
1195 struct ast_channel *chan;
1196 const char *moh_class;
1197 RAII_VAR(struct agent_pvt *, agent, NULL, ao2_cleanup);
1199 chan = bridge_channel->chan;
1201 agent = ao2_find(agents, swap ? swap->chan : chan, 0);
1203 /* Could not find the agent. */
1207 /* Setup agent entertainment */
1209 moh_class = ast_strdupa(agent->cfg->moh);
1210 agent_unlock(agent);
1211 res |= ast_channel_add_bridge_role(chan, "holding_participant");
1212 res |= ast_channel_set_bridge_role_option(chan, "holding_participant", "idle_mode", "musiconhold");
1213 res |= ast_channel_set_bridge_role_option(chan, "holding_participant", "moh_class", moh_class);
1215 /* Add DTMF acknowledge hook. */
1218 if (ast_test_flag(agent, AGENT_FLAG_ACK_CALL)
1219 ? agent->override_ack_call : agent->cfg->ack_call) {
1220 const char *dtmf_accept;
1222 dtmf_accept = ast_test_flag(agent, AGENT_FLAG_DTMF_ACCEPT)
1223 ? agent->override_dtmf_accept : agent->cfg->dtmf_accept;
1224 ast_copy_string(dtmf, dtmf_accept, sizeof(dtmf));
1226 agent_unlock(agent);
1227 if (!ast_strlen_zero(dtmf)) {
1229 if (ast_bridge_dtmf_hook(bridge_channel->features, dtmf, bridge_agent_hold_ack,
1230 agent, __ao2_cleanup, AST_BRIDGE_HOOK_REMOVE_ON_PULL)) {
1236 /* Add heartbeat interval hook. */
1238 if (ast_bridge_interval_hook(bridge_channel->features, 0, 1000,
1239 bridge_agent_hold_heartbeat, agent, __ao2_cleanup, AST_BRIDGE_HOOK_REMOVE_ON_PULL)) {
1244 res |= ast_bridge_base_v_table.push(self, bridge_channel, swap);
1246 ast_channel_remove_bridge_role(chan, "holding_participant");
1251 res = ast_bridge_set_after_callback(chan, agent_after_bridge_cb,
1252 agent_after_bridge_cb_failed, chan);
1254 ast_channel_remove_bridge_role(chan, "holding_participant");
1259 ast_channel_unref(agent->logged);
1260 agent->logged = ast_channel_ref(chan);
1261 agent_unlock(agent);
1264 * Kick the channel out so it can come back in fully controlled.
1265 * Otherwise, the after bridge callback will linger and the
1266 * agent will have some slightly different behavior in corner
1269 ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END,
1270 AST_CAUSE_NORMAL_CLEARING);
1275 switch (agent->state) {
1276 case AGENT_STATE_LOGGED_OUT:
1278 * \todo XXX the login probation time should be only if it is needed.
1280 * Need to determine if there are any local channels that can
1281 * optimize and wait until they actually do before leaving the
1282 * AGENT_STATE_PROBATION_WAIT state. For now, the blind
1283 * timer of LOGIN_WAIT_TIMEOUT_TIME will do.
1286 * Start the login probation timer.
1288 * We cannot handle an agent local channel optimization when the
1289 * agent is on a call. The optimization may kick the agent
1290 * channel we know about out of the call without our being able
1291 * to switch to the replacement channel. Get any agent local
1292 * channel optimization out of the way while the agent is in the
1295 time(&agent->probation_start);
1296 agent->state = AGENT_STATE_PROBATION_WAIT;
1297 agent_unlock(agent);
1299 case AGENT_STATE_PROBATION_WAIT:
1300 /* Restart the probation timer. */
1301 time(&agent->probation_start);
1302 agent_unlock(agent);
1304 case AGENT_STATE_READY_FOR_CALL:
1306 * Likely someone manually kicked us out of the holding bridge
1307 * and we came right back in.
1309 agent_unlock(agent);
1312 /* Unexpected agent state. */
1315 case AGENT_STATE_CALL_PRESENT:
1316 case AGENT_STATE_CALL_WAIT_ACK:
1317 agent->state = AGENT_STATE_READY_FOR_CALL;
1318 agent->devstate = AST_DEVICE_NOT_INUSE;
1319 agent_unlock(agent);
1320 ast_debug(1, "Agent %s: Call abort recovery complete.\n", agent->username);
1321 agent_devstate_changed(agent->username);
1323 case AGENT_STATE_ON_CALL:
1324 case AGENT_STATE_CALL_WRAPUP:
1325 wrapup_time = agent->cfg->wrapup_time;
1326 if (ast_test_flag(agent, AGENT_FLAG_WRAPUP_TIME)) {
1327 wrapup_time = agent->override_wrapup_time;
1330 agent->state = AGENT_STATE_CALL_WRAPUP;
1332 agent->state = AGENT_STATE_READY_FOR_CALL;
1333 agent->devstate = AST_DEVICE_NOT_INUSE;
1335 agent_unlock(agent);
1337 /* No wrapup time. */
1338 ast_debug(1, "Agent %s: Ready for new call.\n", agent->username);
1339 agent_devstate_changed(agent->username);
1349 * \brief ast_bridge agent_hold pull method.
1351 * \param self Bridge to operate upon.
1352 * \param bridge_channel Bridge channel to pull.
1355 * Remove any channel hooks controlled by the bridge. Release
1356 * any resources held by bridge_channel->bridge_pvt and release
1357 * bridge_channel->bridge_pvt.
1359 * \note On entry, self is already locked.
1363 static void bridge_agent_hold_pull(struct ast_bridge *self, struct ast_bridge_channel *bridge_channel)
1365 ast_channel_remove_bridge_role(bridge_channel->chan, "holding_participant");
1366 ast_bridge_base_v_table.pull(self, bridge_channel);
1370 * \brief The bridge is being dissolved.
1372 * \param self Bridge to operate upon.
1375 * The bridge is being dissolved. Remove any external
1376 * references to the bridge so it can be destroyed.
1378 * \note On entry, self must NOT be locked.
1382 static void bridge_agent_hold_dissolving(struct ast_bridge *self)
1384 ao2_global_obj_release(agent_holding);
1385 ast_bridge_base_v_table.dissolving(self);
1388 static struct ast_bridge_methods bridge_agent_hold_v_table;
1390 static struct ast_bridge *bridge_agent_hold_new(void)
1392 struct ast_bridge *bridge;
1394 bridge = bridge_alloc(sizeof(struct ast_bridge), &bridge_agent_hold_v_table);
1395 bridge = bridge_base_init(bridge, AST_BRIDGE_CAPABILITY_HOLDING,
1396 AST_BRIDGE_FLAG_MERGE_INHIBIT_TO | AST_BRIDGE_FLAG_MERGE_INHIBIT_FROM
1397 | AST_BRIDGE_FLAG_SWAP_INHIBIT_FROM | AST_BRIDGE_FLAG_TRANSFER_PROHIBITED,
1398 "AgentPool", NULL, NULL);
1399 bridge = bridge_register(bridge);
1403 static void bridge_init_agent_hold(void)
1405 /* Setup bridge agent_hold subclass v_table. */
1406 bridge_agent_hold_v_table = ast_bridge_base_v_table;
1407 bridge_agent_hold_v_table.name = "agent_hold";
1408 bridge_agent_hold_v_table.dissolving = bridge_agent_hold_dissolving;
1409 bridge_agent_hold_v_table.push = bridge_agent_hold_push;
1410 bridge_agent_hold_v_table.pull = bridge_agent_hold_pull;
1413 static int bridge_agent_hold_deferred_create(void)
1415 RAII_VAR(struct ast_bridge *, holding, ao2_global_obj_ref(agent_holding), ao2_cleanup);
1418 ast_mutex_lock(&agent_holding_lock);
1419 holding = ao2_global_obj_ref(agent_holding);
1421 holding = bridge_agent_hold_new();
1422 ao2_global_obj_replace_unref(agent_holding, holding);
1424 ast_mutex_unlock(&agent_holding_lock);
1426 ast_log(LOG_ERROR, "Could not create agent holding bridge.\n");
1433 static void send_agent_login(struct ast_channel *chan, const char *agent)
1435 RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
1437 ast_assert(agent != NULL);
1439 blob = ast_json_pack("{s: s}",
1445 ast_channel_publish_cached_blob(chan, ast_channel_agent_login_type(), blob);
1448 static void send_agent_logoff(struct ast_channel *chan, const char *agent, long logintime)
1450 RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
1452 ast_assert(agent != NULL);
1454 blob = ast_json_pack("{s: s, s: i}",
1456 "logintime", logintime);
1461 ast_channel_publish_cached_blob(chan, ast_channel_agent_logoff_type(), blob);
1466 * \brief Logout the agent.
1469 * \param agent Which agent logging out.
1471 * \note On entry agent is already locked. On exit it is no longer locked.
1475 static void agent_logout(struct agent_pvt *agent)
1477 struct ast_channel *logged;
1478 struct ast_bridge *caller_bridge;
1479 long time_logged_in;
1481 time_logged_in = time(NULL) - agent->login_start;
1482 logged = agent->logged;
1483 agent->logged = NULL;
1484 caller_bridge = agent->caller_bridge;
1485 agent->caller_bridge = NULL;
1486 agent->state = AGENT_STATE_LOGGED_OUT;
1487 agent->devstate = AST_DEVICE_UNAVAILABLE;
1488 ast_clear_flag(agent, AST_FLAGS_ALL);
1489 agent_unlock(agent);
1490 agent_devstate_changed(agent->username);
1492 if (caller_bridge) {
1493 ast_bridge_destroy(caller_bridge, 0);
1496 ast_channel_lock(logged);
1497 send_agent_logoff(logged, agent->username, time_logged_in);
1498 ast_channel_unlock(logged);
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 ast_channel_hangupcause_set(logged, AST_CAUSE_NORMAL_CLEARING);
1520 goto agent_run_cleanup;
1523 struct agents_cfg *cfgs;
1524 struct agent_cfg *cfg_new;
1525 struct agent_cfg *cfg_old;
1526 struct ast_bridge *holding;
1527 struct ast_bridge *caller_bridge;
1529 ast_channel_hangupcause_set(logged, AST_CAUSE_NORMAL_CLEARING);
1531 holding = ao2_global_obj_ref(agent_holding);
1533 ast_debug(1, "Agent %s: Someone destroyed the agent holding bridge.\n",
1539 * When the agent channel leaves the bridging system we usually
1540 * want to put the agent back into the holding bridge for the
1543 ast_bridge_join(holding, logged, NULL, &features, NULL,
1544 AST_BRIDGE_JOIN_PASS_REFERENCE);
1545 if (logged != agent->logged) {
1546 /* This channel is no longer the logged in agent. */
1551 /* The agent is no longer configured. */
1555 /* Update the agent's config before rejoining the holding bridge. */
1556 cfgs = ao2_global_obj_ref(cfg_handle);
1558 /* There is no agent configuration. All agents were destroyed. */
1561 cfg_new = ao2_find(cfgs->agents, agent->username, OBJ_KEY);
1564 /* The agent is no longer configured. */
1568 cfg_old = agent->cfg;
1569 agent->cfg = cfg_new;
1571 agent->last_disconnect = ast_tvnow();
1573 /* Clear out any caller bridge before rejoining the holding bridge. */
1574 caller_bridge = agent->caller_bridge;
1575 agent->caller_bridge = NULL;
1576 agent_unlock(agent);
1577 ao2_ref(cfg_old, -1);
1578 if (caller_bridge) {
1579 ast_bridge_destroy(caller_bridge, 0);
1582 if (agent->state == AGENT_STATE_LOGGING_OUT
1583 || agent->deferred_logoff
1584 || ast_check_hangup_locked(logged)) {
1585 /* The agent was requested to logout or hungup. */
1590 * It is safe to access agent->waiting_colp without a lock. It
1591 * is only setup on agent login and not changed.
1593 ast_channel_update_connected_line(logged, &agent->waiting_colp, NULL);
1595 ast_bridge_features_cleanup(&features);
1599 if (logged != agent->logged) {
1601 * We are no longer the agent channel because of local channel
1604 agent_unlock(agent);
1605 ast_debug(1, "Agent %s: Channel %s is no longer the agent.\n",
1606 agent->username, ast_channel_name(logged));
1609 agent_logout(agent);
1612 static void agent_after_bridge_cb(struct ast_channel *chan, void *data)
1614 struct agent_pvt *agent;
1616 agent = ao2_find(agents, chan, 0);
1621 ast_debug(1, "Agent %s: New agent channel %s.\n",
1622 agent->username, ast_channel_name(chan));
1623 agent_run(agent, chan);
1627 static void agent_after_bridge_cb_failed(enum ast_bridge_after_cb_reason reason, void *data)
1629 struct ast_channel *chan = data;
1630 struct agent_pvt *agent;
1632 agent = ao2_find(agents, chan, 0);
1636 ast_log(LOG_WARNING, "Agent %s: Forced logout. Lost control of %s because: %s\n",
1637 agent->username, ast_channel_name(chan),
1638 ast_bridge_after_cb_reason_string(reason));
1640 agent_logout(agent);
1646 * \brief Get the lock on the agent bridge_channel and return it.
1649 * \param agent Whose bridge_channel to get.
1651 * \retval bridge_channel on success (Reffed and locked).
1652 * \retval NULL on error.
1654 static struct ast_bridge_channel *agent_bridge_channel_get_lock(struct agent_pvt *agent)
1656 struct ast_channel *logged;
1657 struct ast_bridge_channel *bc;
1661 logged = agent->logged;
1663 agent_unlock(agent);
1666 ast_channel_ref(logged);
1667 agent_unlock(agent);
1669 ast_channel_lock(logged);
1670 bc = ast_channel_get_bridge_channel(logged);
1671 ast_channel_unlock(logged);
1672 ast_channel_unref(logged);
1674 if (agent->logged != logged) {
1680 ast_bridge_channel_lock(bc);
1681 if (bc->chan != logged || agent->logged != logged) {
1682 ast_bridge_channel_unlock(bc);
1690 static void caller_abort_agent(struct agent_pvt *agent)
1692 struct ast_bridge_channel *logged;
1694 logged = agent_bridge_channel_get_lock(agent);
1696 struct ast_bridge *caller_bridge;
1698 ast_debug(1, "Agent '%s' no longer logged in.\n", agent->username);
1701 caller_bridge = agent->caller_bridge;
1702 agent->caller_bridge = NULL;
1703 agent_unlock(agent);
1704 if (caller_bridge) {
1705 ast_bridge_destroy(caller_bridge, 0);
1710 /* Kick the agent out of the holding bridge to reset it. */
1711 ast_bridge_channel_leave_bridge_nolock(logged, BRIDGE_CHANNEL_STATE_END,
1712 AST_CAUSE_NORMAL_CLEARING);
1713 ast_bridge_channel_unlock(logged);
1716 static int caller_safety_timeout(struct ast_bridge_channel *bridge_channel, void *hook_pvt)
1718 struct agent_pvt *agent = hook_pvt;
1720 if (agent->state == AGENT_STATE_CALL_PRESENT) {
1721 ast_log(LOG_WARNING, "Agent '%s' process did not respond. Safety timeout.\n",
1723 pbx_builtin_setvar_helper(bridge_channel->chan, "AGENT_STATUS", "ERROR");
1725 ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END, 0);
1726 caller_abort_agent(agent);
1732 static void agent_alert(struct ast_bridge_channel *bridge_channel, const void *payload, size_t payload_size)
1734 const char *agent_id = payload;
1735 const char *playfile;
1736 const char *dtmf_accept;
1737 struct agent_pvt *agent;
1741 agent = ao2_find(agents, agent_id, OBJ_KEY);
1743 ast_debug(1, "Agent '%s' does not exist. Where did it go?\n", agent_id);
1747 /* Change holding bridge participant role's idle mode to silence */
1748 ast_bridge_channel_lock_bridge(bridge_channel);
1749 ast_bridge_channel_clear_roles(bridge_channel);
1750 ast_channel_set_bridge_role_option(bridge_channel->chan, "holding_participant", "idle_mode", "silence");
1751 ast_bridge_channel_establish_roles(bridge_channel);
1752 ast_bridge_unlock(bridge_channel->bridge);
1755 playfile = ast_strdupa(agent->cfg->beep_sound);
1757 /* Determine which DTMF digits interrupt the alerting signal. */
1758 if (ast_test_flag(agent, AGENT_FLAG_ACK_CALL)
1759 ? agent->override_ack_call : agent->cfg->ack_call) {
1760 dtmf_accept = ast_test_flag(agent, AGENT_FLAG_DTMF_ACCEPT)
1761 ? agent->override_dtmf_accept : agent->cfg->dtmf_accept;
1763 /* Only the first digit of the ack will stop playback. */
1764 dtmf[0] = *dtmf_accept;
1770 agent_unlock(agent);
1772 /* Alert the agent. */
1773 digit = ast_stream_and_wait(bridge_channel->chan, playfile,
1774 ast_strlen_zero(dtmf_accept) ? AST_DIGIT_ANY : dtmf_accept);
1775 ast_stopstream(bridge_channel->chan);
1778 switch (agent->state) {
1779 case AGENT_STATE_CALL_PRESENT:
1780 if (!ast_strlen_zero(dtmf_accept)) {
1781 agent->state = AGENT_STATE_CALL_WAIT_ACK;
1782 agent->ack_time = ast_tvnow();
1785 /* Playback was interrupted by a digit. */
1786 agent_unlock(agent);
1788 ast_bridge_channel_feature_digit(bridge_channel, digit);
1794 /* Connect to caller now. */
1795 ast_debug(1, "Agent %s: Immediately connecting to call.\n", agent->username);
1796 agent_connect_caller(bridge_channel, agent);/* Will unlock agent. */
1802 agent_unlock(agent);
1806 static int send_alert_to_agent(struct ast_bridge_channel *bridge_channel, const char *agent_id)
1808 return ast_bridge_channel_queue_callback(bridge_channel,
1809 AST_BRIDGE_CHANNEL_CB_OPTION_MEDIA, agent_alert, agent_id, strlen(agent_id) + 1);
1812 static int send_colp_to_agent(struct ast_bridge_channel *bridge_channel, struct ast_party_connected_line *connected)
1814 struct ast_set_party_connected_line update = {
1819 unsigned char data[1024]; /* This should be large enough */
1822 datalen = ast_connected_line_build_data(data, sizeof(data), connected, &update);
1823 if (datalen == (size_t) -1) {
1827 return ast_bridge_channel_queue_control_data(bridge_channel,
1828 AST_CONTROL_CONNECTED_LINE, data, datalen);
1833 * \brief Caller joined the bridge event callback.
1835 * \param bridge_channel Channel executing the feature
1836 * \param hook_pvt Private data passed in when the hook was created
1838 * \retval 0 Keep the callback hook.
1839 * \retval -1 Remove the callback hook.
1841 static int caller_joined_bridge(struct ast_bridge_channel *bridge_channel, void *hook_pvt)
1843 struct agent_pvt *agent = hook_pvt;
1844 struct ast_bridge_channel *logged;
1847 logged = agent_bridge_channel_get_lock(agent);
1849 ast_verb(3, "Agent '%s' not logged in.\n", agent->username);
1850 pbx_builtin_setvar_helper(bridge_channel->chan, "AGENT_STATUS", "NOT_LOGGED_IN");
1852 ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END, 0);
1853 caller_abort_agent(agent);
1857 res = send_alert_to_agent(logged, agent->username);
1858 ast_bridge_channel_unlock(logged);
1859 ao2_ref(logged, -1);
1861 ast_verb(3, "Agent '%s': Failed to alert the agent.\n", agent->username);
1862 pbx_builtin_setvar_helper(bridge_channel->chan, "AGENT_STATUS", "ERROR");
1864 ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END, 0);
1865 caller_abort_agent(agent);
1869 pbx_builtin_setvar_helper(bridge_channel->chan, "AGENT_STATUS", "NOT_CONNECTED");
1870 ast_indicate(bridge_channel->chan, AST_CONTROL_RINGING);
1875 * \brief Dialplan AgentRequest application to locate an agent to talk with.
1877 * \param chan Channel wanting to talk with an agent.
1878 * \param data Application parameters
1880 * \retval 0 To continue in dialplan.
1881 * \retval -1 To hangup.
1883 static int agent_request_exec(struct ast_channel *chan, const char *data)
1885 struct ast_bridge *caller_bridge;
1886 struct ast_bridge_channel *logged;
1889 struct ast_bridge_features caller_features;
1890 struct ast_party_connected_line connected;
1891 AST_DECLARE_APP_ARGS(args,
1892 AST_APP_ARG(agent_id);
1893 AST_APP_ARG(other); /* Any remaining unused arguments */
1896 RAII_VAR(struct agent_pvt *, agent, NULL, ao2_cleanup);
1898 if (bridge_agent_hold_deferred_create()) {
1902 parse = ast_strdupa(data);
1903 AST_STANDARD_APP_ARGS(args, parse);
1905 if (ast_strlen_zero(args.agent_id)) {
1906 ast_log(LOG_WARNING, "AgentRequest requires an AgentId\n");
1910 /* Find the agent. */
1911 agent = ao2_find(agents, args.agent_id, OBJ_KEY);
1913 ast_verb(3, "Agent '%s' does not exist.\n", args.agent_id);
1914 pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "INVALID");
1918 if (ast_bridge_features_init(&caller_features)) {
1922 /* Add safety timeout hook. */
1924 if (ast_bridge_interval_hook(&caller_features, 0, CALLER_SAFETY_TIMEOUT_TIME,
1925 caller_safety_timeout, agent, __ao2_cleanup, AST_BRIDGE_HOOK_REMOVE_ON_PULL)) {
1927 ast_bridge_features_cleanup(&caller_features);
1931 /* Setup the alert agent on caller joining the bridge hook. */
1933 if (ast_bridge_join_hook(&caller_features, caller_joined_bridge, agent,
1934 __ao2_cleanup, 0)) {
1936 ast_bridge_features_cleanup(&caller_features);
1940 caller_bridge = ast_bridge_basic_new();
1941 if (!caller_bridge) {
1942 ast_bridge_features_cleanup(&caller_features);
1947 switch (agent->state) {
1948 case AGENT_STATE_LOGGED_OUT:
1949 case AGENT_STATE_LOGGING_OUT:
1950 agent_unlock(agent);
1951 ast_bridge_destroy(caller_bridge, 0);
1952 ast_bridge_features_cleanup(&caller_features);
1953 ast_verb(3, "Agent '%s' not logged in.\n", agent->username);
1954 pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "NOT_LOGGED_IN");
1956 case AGENT_STATE_READY_FOR_CALL:
1957 ao2_ref(caller_bridge, +1);
1958 agent->caller_bridge = caller_bridge;
1959 agent->state = AGENT_STATE_CALL_PRESENT;
1960 agent->devstate = AST_DEVICE_INUSE;
1963 agent_unlock(agent);
1964 ast_bridge_destroy(caller_bridge, 0);
1965 ast_bridge_features_cleanup(&caller_features);
1966 ast_verb(3, "Agent '%s' is busy.\n", agent->username);
1967 pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "BUSY");
1970 agent_unlock(agent);
1971 agent_devstate_changed(agent->username);
1973 /* Get COLP for agent. */
1974 ast_party_connected_line_init(&connected);
1975 ast_channel_lock(chan);
1976 ast_connected_line_copy_from_caller(&connected, ast_channel_caller(chan));
1977 ast_channel_unlock(chan);
1979 logged = agent_bridge_channel_get_lock(agent);
1981 ast_party_connected_line_free(&connected);
1982 caller_abort_agent(agent);
1983 ast_bridge_destroy(caller_bridge, 0);
1984 ast_bridge_features_cleanup(&caller_features);
1985 ast_verb(3, "Agent '%s' not logged in.\n", agent->username);
1986 pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "NOT_LOGGED_IN");
1990 send_colp_to_agent(logged, &connected);
1991 ast_bridge_channel_unlock(logged);
1992 ao2_ref(logged, -1);
1993 ast_party_connected_line_free(&connected);
1995 if (ast_bridge_join(caller_bridge, chan, NULL, &caller_features, NULL,
1996 AST_BRIDGE_JOIN_PASS_REFERENCE)) {
1997 caller_abort_agent(agent);
1998 ast_verb(3, "Agent '%s': Caller %s failed to join the bridge.\n",
1999 agent->username, ast_channel_name(chan));
2000 pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "ERROR");
2002 ast_bridge_features_cleanup(&caller_features);
2004 /* Determine if we need to continue in the dialplan after the bridge. */
2005 ast_channel_lock(chan);
2006 if (ast_channel_softhangup_internal_flag(chan) & AST_SOFTHANGUP_ASYNCGOTO) {
2008 * The bridge was broken for a hangup that isn't real.
2009 * Don't run the h extension, because the channel isn't
2010 * really hung up. This should really only happen with
2011 * AST_SOFTHANGUP_ASYNCGOTO.
2015 res = ast_check_hangup(chan)
2016 || ast_test_flag(ast_channel_flags(chan), AST_FLAG_ZOMBIE)
2017 || ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENT_STATUS"));
2019 ast_channel_unlock(chan);
2021 return res ? -1 : 0;
2026 * \brief Get agent config values from the login channel.
2029 * \param agent What to setup channel config values on.
2030 * \param chan Channel logging in as an agent.
2034 static void agent_login_channel_config(struct agent_pvt *agent, struct ast_channel *chan)
2036 struct ast_flags opts = { 0 };
2037 struct ast_party_connected_line connected;
2038 unsigned int override_ack_call = 0;
2039 unsigned int override_auto_logoff = 0;
2040 unsigned int override_wrapup_time = 0;
2041 const char *override_dtmf_accept = NULL;
2044 ast_party_connected_line_init(&connected);
2046 /* Get config values from channel. */
2047 ast_channel_lock(chan);
2048 ast_party_connected_line_copy(&connected, ast_channel_connected(chan));
2050 var = pbx_builtin_getvar_helper(chan, "AGENTACKCALL");
2051 if (!ast_strlen_zero(var)) {
2052 override_ack_call = ast_true(var) ? 1 : 0;
2053 ast_set_flag(&opts, AGENT_FLAG_ACK_CALL);
2056 var = pbx_builtin_getvar_helper(chan, "AGENTACCEPTDTMF");
2057 if (!ast_strlen_zero(var)) {
2058 override_dtmf_accept = ast_strdupa(var);
2059 ast_set_flag(&opts, AGENT_FLAG_DTMF_ACCEPT);
2062 var = pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF");
2063 if (!ast_strlen_zero(var)) {
2064 if (sscanf(var, "%u", &override_auto_logoff) == 1) {
2065 ast_set_flag(&opts, AGENT_FLAG_AUTO_LOGOFF);
2069 var = pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME");
2070 if (!ast_strlen_zero(var)) {
2071 if (sscanf(var, "%u", &override_wrapup_time) == 1) {
2072 ast_set_flag(&opts, AGENT_FLAG_WRAPUP_TIME);
2075 ast_channel_unlock(chan);
2077 /* Set config values on agent. */
2079 ast_party_connected_line_free(&agent->waiting_colp);
2080 agent->waiting_colp = connected;
2082 ast_string_field_set(agent, override_dtmf_accept, override_dtmf_accept);
2083 ast_copy_flags(agent, &opts, AST_FLAGS_ALL);
2084 agent->override_auto_logoff = override_auto_logoff;
2085 agent->override_wrapup_time = override_wrapup_time;
2086 agent->override_ack_call = override_ack_call;
2087 agent_unlock(agent);
2090 enum AGENT_LOGIN_OPT_FLAGS {
2091 OPT_SILENT = (1 << 0),
2093 AST_APP_OPTIONS(agent_login_opts, BEGIN_OPTIONS
2094 AST_APP_OPTION('s', OPT_SILENT),
2098 * \brief Dialplan AgentLogin application to log in an agent.
2100 * \param chan Channel attempting to login as an agent.
2101 * \param data Application parameters
2103 * \retval 0 To continue in dialplan.
2104 * \retval -1 To hangup.
2106 static int agent_login_exec(struct ast_channel *chan, const char *data)
2109 struct ast_flags opts;
2110 AST_DECLARE_APP_ARGS(args,
2111 AST_APP_ARG(agent_id);
2112 AST_APP_ARG(options);
2113 AST_APP_ARG(other); /* Any remaining unused arguments */
2116 RAII_VAR(struct agent_pvt *, agent, NULL, ao2_cleanup);
2118 if (bridge_agent_hold_deferred_create()) {
2122 if (ast_channel_state(chan) != AST_STATE_UP && ast_answer(chan)) {
2126 parse = ast_strdupa(data);
2127 AST_STANDARD_APP_ARGS(args, parse);
2129 if (ast_strlen_zero(args.agent_id)) {
2130 ast_log(LOG_WARNING, "AgentLogin requires an AgentId\n");
2134 if (ast_app_parse_options(agent_login_opts, &opts, NULL, args.options)) {
2135 /* General invalid option syntax. */
2139 /* Find the agent. */
2140 agent = ao2_find(agents, args.agent_id, OBJ_KEY);
2142 ast_verb(3, "Agent '%s' does not exist.\n", args.agent_id);
2143 pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "INVALID");
2147 /* Has someone already logged in as this agent already? */
2149 if (agent->logged) {
2150 agent_unlock(agent);
2151 ast_verb(3, "Agent '%s' already logged in.\n", agent->username);
2152 pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "ALREADY_LOGGED_IN");
2155 agent->logged = ast_channel_ref(chan);
2156 agent->last_disconnect = ast_tvnow();
2157 time(&agent->login_start);
2158 agent->deferred_logoff = 0;
2159 agent_unlock(agent);
2161 agent_login_channel_config(agent, chan);
2163 if (!ast_test_flag(&opts, OPT_SILENT)) {
2164 ast_stream_and_wait(chan, "agent-loginok", AST_DIGIT_NONE);
2167 ast_verb(2, "Agent '%s' logged in (format %s/%s)\n", agent->username,
2168 ast_format_get_name(ast_channel_readformat(chan)),
2169 ast_format_get_name(ast_channel_writeformat(chan)));
2170 ast_channel_lock(chan);
2171 send_agent_login(chan, agent->username);
2172 ast_channel_unlock(chan);
2174 agent_run(agent, chan);
2178 static int agent_function_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
2181 struct agent_pvt *agent;
2182 struct ast_channel *logged;
2183 AST_DECLARE_APP_ARGS(args,
2184 AST_APP_ARG(agentid);
2190 parse = ast_strdupa(data ?: "");
2191 AST_NONSTANDARD_APP_ARGS(args, parse, ':');
2193 if (ast_strlen_zero(args.agentid)) {
2194 ast_log(LOG_WARNING, "The AGENT function requires an argument - agentid!\n");
2198 args.item = "status";
2201 agent = ao2_find(agents, args.agentid, OBJ_KEY);
2203 ast_log(LOG_WARNING, "Agent '%s' not found!\n", args.agentid);
2208 if (!strcasecmp(args.item, "status")) {
2211 if (agent->logged) {
2212 status = "LOGGEDIN";
2214 status = "LOGGEDOUT";
2216 ast_copy_string(buf, status, len);
2217 } else if (!strcasecmp(args.item, "name")) {
2218 ast_copy_string(buf, agent->cfg->full_name, len);
2219 } else if (!strcasecmp(args.item, "mohclass")) {
2220 ast_copy_string(buf, agent->cfg->moh, len);
2221 } else if (!strcasecmp(args.item, "channel")) {
2222 logged = agent_lock_logged(agent);
2226 ast_copy_string(buf, ast_channel_name(logged), len);
2227 ast_channel_unlock(logged);
2228 ast_channel_unref(logged);
2230 pos = strrchr(buf, '-');
2235 } else if (!strcasecmp(args.item, "fullchannel")) {
2236 logged = agent_lock_logged(agent);
2238 ast_copy_string(buf, ast_channel_name(logged), len);
2239 ast_channel_unlock(logged);
2240 ast_channel_unref(logged);
2243 agent_unlock(agent);
2249 static struct ast_custom_function agent_function = {
2251 .read = agent_function_read,
2254 struct agent_complete {
2255 /*! Nth match to return. */
2257 /*! Which match currently on. */
2261 static int complete_agent_search(void *obj, void *arg, void *data, int flags)
2263 struct agent_complete *search = data;
2265 if (++search->which > search->state) {
2271 static char *complete_agent(const char *word, int state)
2274 struct agent_pvt *agent;
2275 struct agent_complete search = {
2279 agent = ao2_callback_data(agents, ast_strlen_zero(word) ? 0 : OBJ_PARTIAL_KEY,
2280 complete_agent_search, (char *) word, &search);
2284 ret = ast_strdup(agent->username);
2289 static int complete_agent_logoff_search(void *obj, void *arg, void *data, int flags)
2291 struct agent_pvt *agent = obj;
2292 struct agent_complete *search = data;
2294 if (!agent->logged) {
2297 if (++search->which > search->state) {
2303 static char *complete_agent_logoff(const char *word, int state)
2306 struct agent_pvt *agent;
2307 struct agent_complete search = {
2311 agent = ao2_callback_data(agents, ast_strlen_zero(word) ? 0 : OBJ_PARTIAL_KEY,
2312 complete_agent_logoff_search, (char *) word, &search);
2316 ret = ast_strdup(agent->username);
2321 static void agent_show_requested(struct ast_cli_args *a, int online_only)
2323 #define FORMAT_HDR "%-8s %-20s %-11s %-30s %s\n"
2324 #define FORMAT_ROW "%-8s %-20s %-11s %-30s %s\n"
2326 struct ao2_iterator iter;
2327 struct agent_pvt *agent;
2328 struct ast_str *out = ast_str_alloca(512);
2329 unsigned int agents_total = 0;
2330 unsigned int agents_logged_in = 0;
2331 unsigned int agents_talking = 0;
2333 ast_cli(a->fd, FORMAT_HDR, "Agent-ID", "Name", "State", "Channel", "Talking with");
2334 iter = ao2_iterator_init(agents, 0);
2335 for (; (agent = ao2_iterator_next(&iter)); ao2_ref(agent, -1)) {
2336 struct ast_channel *logged;
2341 logged = agent_lock_logged(agent);
2343 const char *talking_with;
2347 talking_with = pbx_builtin_getvar_helper(logged, "BRIDGEPEER");
2348 if (!ast_strlen_zero(talking_with)) {
2353 ast_str_set(&out, 0, FORMAT_ROW, agent->username, agent->cfg->full_name,
2354 ast_devstate_str(agent->devstate), ast_channel_name(logged), talking_with);
2355 ast_channel_unlock(logged);
2356 ast_channel_unref(logged);
2358 ast_str_set(&out, 0, FORMAT_ROW, agent->username, agent->cfg->full_name,
2359 ast_devstate_str(agent->devstate), "", "");
2361 agent_unlock(agent);
2363 if (!online_only || logged) {
2364 ast_cli(a->fd, "%s", ast_str_buffer(out));
2367 ao2_iterator_destroy(&iter);
2369 ast_cli(a->fd, "\nDefined agents: %u, Logged in: %u, Talking: %u\n",
2370 agents_total, agents_logged_in, agents_talking);
2376 static char *agent_handle_show_online(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2380 e->command = "agent show online";
2382 "Usage: agent show online\n"
2383 " Provides summary information for logged in agents.\n";
2390 return CLI_SHOWUSAGE;
2393 agent_show_requested(a, 1);
2398 static char *agent_handle_show_all(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2402 e->command = "agent show all";
2404 "Usage: agent show all\n"
2405 " Provides summary information for all agents.\n";
2412 return CLI_SHOWUSAGE;
2415 agent_show_requested(a, 0);
2420 static char *agent_handle_show_specific(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2422 struct agent_pvt *agent;
2423 struct ast_channel *logged;
2424 struct ast_str *out = ast_str_alloca(4096);
2428 e->command = "agent show";
2430 "Usage: agent show <agent-id>\n"
2431 " Show information about the <agent-id> agent\n";
2435 return complete_agent(a->word, a->n);
2441 return CLI_SHOWUSAGE;
2444 agent = ao2_find(agents, a->argv[2], OBJ_KEY);
2446 ast_cli(a->fd, "Agent '%s' not found\n", a->argv[2]);
2451 logged = agent_lock_logged(agent);
2452 ast_str_set(&out, 0, "Id: %s\n", agent->username);
2453 ast_str_append(&out, 0, "Name: %s\n", agent->cfg->full_name);
2454 ast_str_append(&out, 0, "Beep: %s\n", agent->cfg->beep_sound);
2455 ast_str_append(&out, 0, "MOH: %s\n", agent->cfg->moh);
2456 ast_str_append(&out, 0, "RecordCalls: %s\n", AST_CLI_YESNO(agent->cfg->record_agent_calls));
2457 ast_str_append(&out, 0, "State: %s\n", ast_devstate_str(agent->devstate));
2459 const char *talking_with;
2461 ast_str_append(&out, 0, "LoggedInChannel: %s\n", ast_channel_name(logged));
2462 ast_str_append(&out, 0, "LoggedInTime: %ld\n", (long) agent->login_start);
2463 talking_with = pbx_builtin_getvar_helper(logged, "BRIDGEPEER");
2464 if (!ast_strlen_zero(talking_with)) {
2465 ast_str_append(&out, 0, "TalkingWith: %s\n", talking_with);
2466 ast_str_append(&out, 0, "CallStarted: %ld\n", (long) agent->call_start);
2468 ast_channel_unlock(logged);
2469 ast_channel_unref(logged);
2471 agent_unlock(agent);
2474 ast_cli(a->fd, "%s", ast_str_buffer(out));
2479 static char *agent_handle_logoff_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2483 e->command = "agent logoff";
2485 "Usage: agent logoff <agent-id> [soft]\n"
2486 " Sets an agent as no longer logged in.\n"
2487 " If 'soft' is specified, do not hangup existing calls.\n";
2491 return complete_agent_logoff(a->word, a->n);
2492 } else if (a->pos == 3 && a->n == 0
2493 && (ast_strlen_zero(a->word)
2494 || !strncasecmp("soft", a->word, strlen(a->word)))) {
2495 return ast_strdup("soft");
2500 if (a->argc < 3 || 4 < a->argc) {
2501 return CLI_SHOWUSAGE;
2503 if (a->argc == 4 && strcasecmp(a->argv[3], "soft")) {
2504 return CLI_SHOWUSAGE;
2507 if (!agent_logoff_request(a->argv[2], a->argc == 4)) {
2508 ast_cli(a->fd, "Logging out %s\n", a->argv[2]);
2514 static struct ast_cli_entry cli_agents[] = {
2515 AST_CLI_DEFINE(agent_handle_show_online, "Show status of online agents"),
2516 AST_CLI_DEFINE(agent_handle_show_all, "Show status of all agents"),
2517 AST_CLI_DEFINE(agent_handle_show_specific, "Show information about an agent"),
2518 AST_CLI_DEFINE(agent_handle_logoff_cmd, "Sets an agent offline"),
2521 static int action_agents(struct mansession *s, const struct message *m)
2523 const char *id = astman_get_header(m, "ActionID");
2524 char id_text[AST_MAX_BUF];
2525 struct ao2_iterator iter;
2526 struct agent_pvt *agent;
2527 struct ast_str *out = ast_str_alloca(4096);
2530 if (!ast_strlen_zero(id)) {
2531 snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", id);
2535 astman_send_listack(s, m, "Agents will follow", "start");
2537 iter = ao2_iterator_init(agents, 0);
2538 for (; (agent = ao2_iterator_next(&iter)); ao2_ref(agent, -1)) {
2539 struct ast_channel *logged;
2542 logged = agent_lock_logged(agent);
2546 * AGENT_LOGGEDOFF - Agent isn't logged in
2547 * AGENT_IDLE - Agent is logged in, and waiting for call
2548 * AGENT_ONCALL - Agent is logged in, and on a call
2549 * AGENT_UNKNOWN - Don't know anything about agent. Shouldn't ever get this.
2551 ast_str_set(&out, 0, "Agent: %s\r\n", agent->username);
2552 ast_str_append(&out, 0, "Name: %s\r\n", agent->cfg->full_name);
2555 const char *talking_to_chan;
2556 struct ast_str *logged_headers;
2557 RAII_VAR(struct ast_channel_snapshot *, logged_snapshot, ast_channel_snapshot_create(logged), ao2_cleanup);
2559 if (!logged_snapshot
2560 || !(logged_headers =
2561 ast_manager_build_channel_state_string(logged_snapshot))) {
2562 ast_channel_unlock(logged);
2563 ast_channel_unref(logged);
2564 agent_unlock(agent);
2568 talking_to_chan = pbx_builtin_getvar_helper(logged, "BRIDGEPEER");
2569 if (!ast_strlen_zero(talking_to_chan)) {
2570 ast_str_append(&out, 0, "Status: %s\r\n", "AGENT_ONCALL");
2571 ast_str_append(&out, 0, "TalkingToChan: %s\r\n", talking_to_chan);
2572 ast_str_append(&out, 0, "CallStarted: %ld\n", (long) agent->call_start);
2574 ast_str_append(&out, 0, "Status: %s\r\n", "AGENT_IDLE");
2576 ast_str_append(&out, 0, "LoggedInTime: %ld\r\n", (long) agent->login_start);
2577 ast_str_append(&out, 0, "%s", ast_str_buffer(logged_headers));
2578 ast_channel_unlock(logged);
2579 ast_channel_unref(logged);
2580 ast_free(logged_headers);
2582 ast_str_append(&out, 0, "Status: %s\r\n", "AGENT_LOGGEDOFF");
2585 agent_unlock(agent);
2587 astman_append(s, "Event: Agents\r\n"
2589 ast_str_buffer(out), id_text);
2592 ao2_iterator_destroy(&iter);
2594 astman_send_list_complete_start(s, m, "AgentsComplete", num_agents);
2595 astman_send_list_complete_end(s);
2599 static int action_agent_logoff(struct mansession *s, const struct message *m)
2601 const char *agent = astman_get_header(m, "Agent");
2602 const char *soft_s = astman_get_header(m, "Soft"); /* "true" is don't hangup */
2604 if (ast_strlen_zero(agent)) {
2605 astman_send_error(s, m, "No agent specified");
2609 if (!agent_logoff_request(agent, ast_true(soft_s))) {
2610 astman_send_ack(s, m, "Agent logged out");
2612 astman_send_error(s, m, "No such agent");
2618 static int unload_module(void)
2620 struct ast_bridge *holding;
2622 /* Unregister dialplan applications */
2623 ast_unregister_application(app_agent_login);
2624 ast_unregister_application(app_agent_request);
2626 /* Unregister dialplan functions */
2627 ast_custom_function_unregister(&agent_function);
2629 /* Unregister manager command */
2630 ast_manager_unregister("Agents");
2631 ast_manager_unregister("AgentLogoff");
2633 /* Unregister CLI commands */
2634 ast_cli_unregister_multiple(cli_agents, ARRAY_LEN(cli_agents));
2636 ast_devstate_prov_del("Agent");
2638 /* Destroy agent holding bridge. */
2639 holding = ao2_global_obj_replace(agent_holding, NULL);
2641 ast_bridge_destroy(holding, 0);
2645 ao2_ref(agents, -1);
2650 static int load_module(void)
2654 agents = ao2_container_alloc_rbtree(AO2_ALLOC_OPT_LOCK_MUTEX,
2655 AO2_CONTAINER_ALLOC_OPT_DUPS_REPLACE, agent_pvt_sort_cmp, agent_pvt_cmp);
2657 return AST_MODULE_LOAD_FAILURE;
2659 if (load_config()) {
2660 ast_log(LOG_ERROR, "Unable to load config. Not loading module.\n");
2661 ao2_ref(agents, -1);
2663 return AST_MODULE_LOAD_DECLINE;
2666 /* Init agent holding bridge v_table. */
2667 bridge_init_agent_hold();
2669 /* Setup to provide Agent:agent-id device state. */
2670 res |= ast_devstate_prov_add("Agent", agent_pvt_devstate_get);
2673 res |= ast_cli_register_multiple(cli_agents, ARRAY_LEN(cli_agents));
2675 /* Manager commands */
2676 res |= ast_manager_register_xml("Agents", EVENT_FLAG_AGENT, action_agents);
2677 res |= ast_manager_register_xml("AgentLogoff", EVENT_FLAG_AGENT, action_agent_logoff);
2679 /* Dialplan Functions */
2680 res |= ast_custom_function_register(&agent_function);
2682 /* Dialplan applications */
2683 res |= ast_register_application_xml(app_agent_login, agent_login_exec);
2684 res |= ast_register_application_xml(app_agent_request, agent_request_exec);
2688 return AST_MODULE_LOAD_FAILURE;
2690 return AST_MODULE_LOAD_SUCCESS;
2693 static int reload(void)
2695 if (aco_process_config(&cfg_info, 1) == ACO_PROCESS_ERROR) {
2696 /* Just keep the config we already have in place. */
2702 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Call center agent pool applications",
2703 .support_level = AST_MODULE_SUPPORT_CORE,
2704 .load = load_module,
2705 .unload = unload_module,
2707 .load_pri = AST_MODPRI_DEVSTATE_PROVIDER,