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"
54 <application name="AgentLogin" language="en_US">
59 <parameter name="AgentId" required="true" />
60 <parameter name="options">
63 <para>silent login - do not announce the login ok segment after
64 agent logged on.</para>
70 <para>Login an agent to the system. Any agent authentication is assumed to
71 already be done by dialplan. While logged in, the agent can receive calls
72 and will hear the sound file specified by the config option custom_beep
73 when a new call comes in for the agent. Login failures will continue in
74 the dialplan with <variable>AGENT_STATUS</variable> set.</para>
75 <para>Before logging in, you can setup on the real agent channel the
76 CHANNEL(dtmf-features) an agent will have when talking to a caller
77 and you can setup on the channel running this application the
78 CONNECTEDLINE() information the agent will see while waiting for a
80 <para><variable>AGENT_STATUS</variable> enumeration values:</para>
82 <enum name = "INVALID"><para>The specified agent is invalid.</para></enum>
83 <enum name = "ALREADY_LOGGED_IN"><para>The agent is already logged in.</para></enum>
85 <note><para>The Agents:<replaceable>AgentId</replaceable> device state is
86 available to monitor the status of the agent.</para></note>
89 <ref type="application">Authenticate</ref>
90 <ref type="application">Queue</ref>
91 <ref type="application">AddQueueMember</ref>
92 <ref type="application">RemoveQueueMember</ref>
93 <ref type="application">PauseQueueMember</ref>
94 <ref type="application">UnpauseQueueMember</ref>
95 <ref type="function">AGENT</ref>
96 <ref type="function">CHANNEL(dtmf-features)</ref>
97 <ref type="function">CONNECTEDLINE()</ref>
98 <ref type="filename">agents.conf</ref>
99 <ref type="filename">queues.conf</ref>
102 <application name="AgentRequest" language="en_US">
104 Request an agent to connect with the channel.
107 <parameter name="AgentId" required="true" />
110 <para>Request an agent to connect with the channel. Failure to find and
111 alert an agent will continue in the dialplan with <variable>AGENT_STATUS</variable> set.</para>
112 <para><variable>AGENT_STATUS</variable> enumeration values:</para>
114 <enum name = "INVALID"><para>The specified agent is invalid.</para></enum>
115 <enum name = "NOT_LOGGED_IN"><para>The agent is not available.</para></enum>
116 <enum name = "BUSY"><para>The agent is on another call.</para></enum>
117 <enum name = "ERROR"><para>Alerting the agent failed.</para></enum>
121 <ref type="application">AgentLogin</ref>
124 <function name="AGENT" language="en_US">
126 Gets information about an Agent
129 <parameter name="AgentId" required="true" />
130 <parameter name="item">
131 <para>The valid items to retrieve are:</para>
134 <para>(default) The status of the agent (LOGGEDIN | LOGGEDOUT)</para>
136 <enum name="password">
137 <para>Deprecated. The dialplan handles any agent authentication.</para>
140 <para>The name of the agent</para>
142 <enum name="mohclass">
143 <para>MusicOnHold class</para>
145 <enum name="channel">
146 <para>The name of the active channel for the Agent (AgentLogin)</para>
148 <enum name="fullchannel">
149 <para>The untruncated name of the active channel for the Agent (AgentLogin)</para>
154 <description></description>
156 <manager name="Agents" language="en_US">
158 Lists agents and their status.
161 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
164 <para>Will list info about all defined agents.</para>
167 <ref type="managerEvent">Agents</ref>
168 <ref type="managerEvent">AgentsComplete</ref>
171 <managerEvent language="en_US" name="Agents">
172 <managerEventInstance class="EVENT_FLAG_AGENT">
174 Response event in a series to the Agents AMI action containing
175 information about a defined agent.
178 <parameter name="Agent">
179 <para>Agent ID of the agent.</para>
181 <parameter name="Name">
182 <para>User friendly name of the agent.</para>
184 <parameter name="Status">
185 <para>Current status of the agent.</para>
186 <para>The valid values are:</para>
188 <enum name="AGENT_LOGGEDOFF" />
189 <enum name="AGENT_IDLE" />
190 <enum name="AGENT_ONCALL" />
193 <parameter name="TalkingToChan">
194 <para><variable>BRIDGEPEER</variable> value on agent channel.</para>
195 <para>Present if Status value is <literal>AGENT_ONCALL</literal>.</para>
197 <parameter name="CallStarted">
198 <para>Epoche time when the agent started talking with the caller.</para>
199 <para>Present if Status value is <literal>AGENT_ONCALL</literal>.</para>
201 <parameter name="LoggedInTime">
202 <para>Epoche time when the agent logged in.</para>
203 <para>Present if Status value is <literal>AGENT_IDLE</literal> or <literal>AGENT_ONCALL</literal>.</para>
206 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
209 <para>The channel snapshot is present if the Status value is <literal>AGENT_IDLE</literal> or <literal>AGENT_ONCALL</literal>.</para>
212 <ref type="manager">Agents</ref>
214 </managerEventInstance>
216 <managerEvent language="en_US" name="AgentsComplete">
217 <managerEventInstance class="EVENT_FLAG_AGENT">
219 Final response event in a series of events to the Agents AMI action.
222 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
225 <ref type="manager">Agents</ref>
227 </managerEventInstance>
229 <manager name="AgentLogoff" language="en_US">
231 Sets an agent as no longer logged in.
234 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
235 <parameter name="Agent" required="true">
236 <para>Agent ID of the agent to log off.</para>
238 <parameter name="Soft">
239 <para>Set to <literal>true</literal> to not hangup existing calls.</para>
243 <para>Sets an agent as no longer logged in.</para>
246 <configInfo name="app_agent_pool" language="en_US">
247 <synopsis>Agent pool applications</synopsis>
249 <note><para>Option changes take effect on agent login or after an agent
250 disconnects from a call.</para></note>
252 <configFile name="agents.conf">
253 <configObject name="global">
254 <synopsis>Unused, but reserved.</synopsis>
256 <configObject name="agent-id">
257 <synopsis>Configure an agent for the pool.</synopsis>
259 <xi:include xpointer="xpointer(/docs/configInfo[@name='app_agent_pool']/description/note)" />
261 <configOption name="ackcall">
262 <synopsis>Enable to require the agent to acknowledge a call.</synopsis>
264 <para>Enable to require the agent to give a DTMF acknowledgement
265 when the agent receives a call.</para>
266 <note><para>The option is overridden by <variable>AGENTACKCALL</variable> on agent login.</para></note>
267 <xi:include xpointer="xpointer(/docs/configInfo[@name='app_agent_pool']/description/note)" />
270 <configOption name="acceptdtmf">
271 <synopsis>DTMF key sequence the agent uses to acknowledge a call.</synopsis>
273 <note><para>The option is overridden by <variable>AGENTACCEPTDTMF</variable> on agent login.</para></note>
274 <note><para>The option is ignored unless the ackcall option is enabled.</para></note>
275 <xi:include xpointer="xpointer(/docs/configInfo[@name='app_agent_pool']/description/note)" />
278 <configOption name="autologoff">
279 <synopsis>Time the agent has to acknowledge a call before being logged off.</synopsis>
281 <para>Set how many seconds a call for the agent has to wait for the
282 agent to acknowledge the call before the agent is automatically
283 logged off. If set to zero then the call will wait forever for
284 the agent to acknowledge.</para>
285 <note><para>The option is overridden by <variable>AGENTAUTOLOGOFF</variable> on agent login.</para></note>
286 <note><para>The option is ignored unless the ackcall option is enabled.</para></note>
287 <xi:include xpointer="xpointer(/docs/configInfo[@name='app_agent_pool']/description/note)" />
290 <configOption name="wrapuptime">
291 <synopsis>Minimum time the agent has between calls.</synopsis>
293 <para>Set the minimum amount of time in milliseconds after
294 disconnecting a call before the agent can receive a new call.</para>
295 <note><para>The option is overridden by <variable>AGENTWRAPUPTIME</variable> on agent login.</para></note>
296 <xi:include xpointer="xpointer(/docs/configInfo[@name='app_agent_pool']/description/note)" />
299 <configOption name="musiconhold">
300 <synopsis>Music on hold class the agent listens to between calls.</synopsis>
302 <xi:include xpointer="xpointer(/docs/configInfo[@name='app_agent_pool']/description/note)" />
305 <configOption name="recordagentcalls">
306 <synopsis>Enable to automatically record calls the agent takes.</synopsis>
308 <para>Enable recording calls the agent takes automatically by
309 invoking the automixmon DTMF feature when the agent connects
310 to a caller. See <filename>features.conf.sample</filename> for information about
311 the automixmon feature.</para>
312 <xi:include xpointer="xpointer(/docs/configInfo[@name='app_agent_pool']/description/note)" />
315 <configOption name="custom_beep">
316 <synopsis>Sound file played to alert the agent when a call is present.</synopsis>
318 <xi:include xpointer="xpointer(/docs/configInfo[@name='app_agent_pool']/description/note)" />
321 <configOption name="fullname">
322 <synopsis>A friendly name for the agent used in log messages.</synopsis>
324 <xi:include xpointer="xpointer(/docs/configInfo[@name='app_agent_pool']/description/note)" />
332 /* ------------------------------------------------------------------- */
334 #define AST_MAX_BUF 256
336 /*! Maximum wait time (in ms) for the custom_beep file to play announcing the caller. */
337 #define CALLER_SAFETY_TIMEOUT_TIME (2 * 60 * 1000)
339 /*! Number of seconds to wait for local channel optimizations to complete. */
340 #define LOGIN_WAIT_TIMEOUT_TIME 5
342 static const char app_agent_login[] = "AgentLogin";
343 static const char app_agent_request[] = "AgentRequest";
345 /*! Agent config parameters. */
347 AST_DECLARE_STRING_FIELDS(
348 /*! Identification of the agent. (agents config container key) */
349 AST_STRING_FIELD(username);
350 /*! Name of agent for logging and querying purposes */
351 AST_STRING_FIELD(full_name);
354 * \brief DTMF string for an agent to accept a call.
356 * \note The channel variable AGENTACCEPTDTMF overrides on login.
358 AST_STRING_FIELD(dtmf_accept);
359 /*! Beep sound file to use. Alert the agent a call is waiting. */
360 AST_STRING_FIELD(beep_sound);
361 /*! MOH class to use while agent waiting for call. */
362 AST_STRING_FIELD(moh);
365 * \brief Number of seconds for agent to ack a call before being logged off.
367 * \note The channel variable AGENTAUTOLOGOFF overrides on login.
368 * \note If zero then timer is disabled.
370 unsigned int auto_logoff;
372 * \brief Time after a call in ms before the agent can get a new call.
374 * \note The channel variable AGENTWRAPUPTIME overrides on login.
376 unsigned int wrapup_time;
378 * \brief TRUE if agent needs to ack a call to accept it.
380 * \note The channel variable AGENTACKCALL overrides on login.
383 /*! TRUE if agent calls are automatically recorded. */
384 int record_agent_calls;
389 * \brief Agent config ao2 container sort function.
392 * \param obj_left pointer to the (user-defined part) of an object.
393 * \param obj_right pointer to the (user-defined part) of an object.
394 * \param flags flags from ao2_callback()
395 * OBJ_POINTER - if set, 'obj_right', is an object.
396 * OBJ_KEY - if set, 'obj_right', is a search key item that is not an object.
397 * OBJ_PARTIAL_KEY - if set, 'obj_right', is a partial search key item that is not an object.
399 * \retval <0 if obj_left < obj_right
400 * \retval =0 if obj_left == obj_right
401 * \retval >0 if obj_left > obj_right
403 static int agent_cfg_sort_cmp(const void *obj_left, const void *obj_right, int flags)
405 const struct agent_cfg *cfg_left = obj_left;
406 const struct agent_cfg *cfg_right = obj_right;
407 const char *right_key = obj_right;
410 switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
413 right_key = cfg_right->username;
416 cmp = strcmp(cfg_left->username, right_key);
418 case OBJ_PARTIAL_KEY:
419 cmp = strncmp(cfg_left->username, right_key, strlen(right_key));
425 static void agent_cfg_destructor(void *vdoomed)
427 struct agent_cfg *doomed = vdoomed;
429 ast_string_field_free_memory(doomed);
432 static void *agent_cfg_alloc(const char *name)
434 struct agent_cfg *cfg;
436 cfg = ao2_alloc_options(sizeof(*cfg), agent_cfg_destructor,
437 AO2_ALLOC_OPT_LOCK_NOLOCK);
438 if (!cfg || ast_string_field_init(cfg, 64)) {
441 ast_string_field_set(cfg, username, name);
445 static void *agent_cfg_find(struct ao2_container *agents, const char *username)
447 return ao2_find(agents, username, OBJ_KEY);
450 /*! Agents configuration */
452 /*! Master configured agents container. */
453 struct ao2_container *agents;
456 static struct aco_type agent_type = {
459 .category_match = ACO_BLACKLIST,
460 .category = "^(general|agents)$",
461 .item_alloc = agent_cfg_alloc,
462 .item_find = agent_cfg_find,
463 .item_offset = offsetof(struct agents_cfg, agents),
466 static struct aco_type *agent_types[] = ACO_TYPES(&agent_type);
468 /* The general category is reserved, but unused */
469 static struct aco_type general_type = {
472 .category_match = ACO_WHITELIST,
473 .category = "^general$",
476 static struct aco_file agents_conf = {
477 .filename = "agents.conf",
478 .types = ACO_TYPES(&general_type, &agent_type),
481 static AO2_GLOBAL_OBJ_STATIC(cfg_handle);
483 static void agents_cfg_destructor(void *vdoomed)
485 struct agents_cfg *doomed = vdoomed;
487 ao2_cleanup(doomed->agents);
488 doomed->agents = NULL;
493 * \brief Create struct agents_cfg object.
496 * \note A lock is not needed for the object or any secondary
497 * created cfg objects. These objects are immutable after the
498 * config is loaded and applied.
500 * \retval New struct agents_cfg object.
501 * \retval NULL on error.
503 static void *agents_cfg_alloc(void)
505 struct agents_cfg *cfg;
507 cfg = ao2_alloc_options(sizeof(*cfg), agents_cfg_destructor,
508 AO2_ALLOC_OPT_LOCK_NOLOCK);
512 cfg->agents = ao2_container_alloc_rbtree(AO2_ALLOC_OPT_LOCK_NOLOCK,
513 AO2_CONTAINER_ALLOC_OPT_DUPS_REJECT, agent_cfg_sort_cmp, NULL);
521 static void agents_post_apply_config(void);
523 CONFIG_INFO_STANDARD(cfg_info, cfg_handle, agents_cfg_alloc,
524 .files = ACO_FILES(&agents_conf),
525 .post_apply_config = agents_post_apply_config,
528 static void destroy_config(void)
530 ao2_global_obj_release(cfg_handle);
531 aco_info_destroy(&cfg_info);
534 static int load_config(void)
536 if (aco_info_init(&cfg_info)) {
541 aco_option_register(&cfg_info, "ackcall", ACO_EXACT, agent_types, "no", OPT_BOOL_T, 1, FLDSET(struct agent_cfg, ack_call));
542 aco_option_register(&cfg_info, "acceptdtmf", ACO_EXACT, agent_types, "#", OPT_STRINGFIELD_T, 1, STRFLDSET(struct agent_cfg, dtmf_accept));
543 aco_option_register(&cfg_info, "autologoff", ACO_EXACT, agent_types, "0", OPT_UINT_T, 0, FLDSET(struct agent_cfg, auto_logoff));
544 aco_option_register(&cfg_info, "wrapuptime", ACO_EXACT, agent_types, "0", OPT_UINT_T, 0, FLDSET(struct agent_cfg, wrapup_time));
545 aco_option_register(&cfg_info, "musiconhold", ACO_EXACT, agent_types, "default", OPT_STRINGFIELD_T, 0, STRFLDSET(struct agent_cfg, moh));
546 aco_option_register(&cfg_info, "recordagentcalls", ACO_EXACT, agent_types, "no", OPT_BOOL_T, 1, FLDSET(struct agent_cfg, record_agent_calls));
547 aco_option_register(&cfg_info, "custom_beep", ACO_EXACT, agent_types, "beep", OPT_STRINGFIELD_T, 1, STRFLDSET(struct agent_cfg, beep_sound));
548 aco_option_register(&cfg_info, "fullname", ACO_EXACT, agent_types, NULL, OPT_STRINGFIELD_T, 0, STRFLDSET(struct agent_cfg, full_name));
550 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 RAII_VAR(struct agent_pvt *, agent, ao2_find(agents, agent_id, OBJ_KEY), ao2_cleanup);
732 return agent->devstate;
734 return AST_DEVICE_INVALID;
739 * \brief Request an agent device state be updated.
742 * \param agent_id Which agent needs the device state updated.
746 static void agent_devstate_changed(const char *agent_id)
748 ast_devstate_changed(AST_DEVICE_UNKNOWN, AST_DEVSTATE_CACHABLE, "Agent:%s", agent_id);
751 static void agent_pvt_destructor(void *vdoomed)
753 struct agent_pvt *doomed = vdoomed;
755 /* Make sure device state reflects agent destruction. */
756 if (!ast_strlen_zero(doomed->username)) {
757 ast_debug(1, "Agent %s: Destroyed.\n", doomed->username);
758 agent_devstate_changed(doomed->username);
761 ast_party_connected_line_free(&doomed->waiting_colp);
762 if (doomed->caller_bridge) {
763 ast_bridge_destroy(doomed->caller_bridge);
764 doomed->caller_bridge = NULL;
766 if (doomed->logged) {
767 doomed->logged = ast_channel_unref(doomed->logged);
769 ao2_cleanup(doomed->cfg);
771 ast_string_field_free_memory(doomed);
774 static struct agent_pvt *agent_pvt_new(struct agent_cfg *cfg)
776 struct agent_pvt *agent;
778 agent = ao2_alloc(sizeof(*agent), agent_pvt_destructor);
782 if (ast_string_field_init(agent, 32)) {
786 ast_string_field_set(agent, username, cfg->username);
787 ast_party_connected_line_init(&agent->waiting_colp);
790 agent->devstate = AST_DEVICE_UNAVAILABLE;
796 * \brief Agents ao2 container sort function.
799 * \param obj_left pointer to the (user-defined part) of an object.
800 * \param obj_right pointer to the (user-defined part) of an object.
801 * \param flags flags from ao2_callback()
802 * OBJ_POINTER - if set, 'obj_right', is an object.
803 * OBJ_KEY - if set, 'obj_right', is a search key item that is not an object.
804 * OBJ_PARTIAL_KEY - if set, 'obj_right', is a partial search key item that is not an object.
806 * \retval <0 if obj_left < obj_right
807 * \retval =0 if obj_left == obj_right
808 * \retval >0 if obj_left > obj_right
810 static int agent_pvt_sort_cmp(const void *obj_left, const void *obj_right, int flags)
812 const struct agent_pvt *agent_left = obj_left;
813 const struct agent_pvt *agent_right = obj_right;
814 const char *right_key = obj_right;
817 switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
820 right_key = agent_right->username;
823 cmp = strcmp(agent_left->username, right_key);
825 case OBJ_PARTIAL_KEY:
826 cmp = strncmp(agent_left->username, right_key, strlen(right_key));
834 * \brief ao2_find() callback function.
838 * found = ao2_find(agents, agent, OBJ_POINTER);
839 * found = ao2_find(agents, "agent-id", OBJ_KEY);
840 * found = ao2_find(agents, agent->logged, 0);
842 static int agent_pvt_cmp(void *obj, void *arg, int flags)
844 const struct agent_pvt *agent = obj;
847 switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
850 case OBJ_PARTIAL_KEY:
854 if (agent->logged == arg) {
864 static int agent_mark(void *obj, void *arg, int flags)
866 struct agent_pvt *agent = obj;
874 static void agents_mark(void)
876 ao2_callback(agents, 0, agent_mark, NULL);
879 static int agent_sweep(void *obj, void *arg, int flags)
881 struct agent_pvt *agent = obj;
885 if (agent->the_mark) {
888 /* Unlink dead agents immediately. */
895 static void agents_sweep(void)
897 struct ao2_iterator *iter;
898 struct agent_pvt *agent;
899 struct ast_channel *logged;
901 iter = ao2_callback(agents, OBJ_MULTIPLE | OBJ_UNLINK, agent_sweep, NULL);
905 for (; (agent = ao2_iterator_next(iter)); ao2_ref(agent, -1)) {
908 logged = ast_channel_ref(agent->logged);
917 "Forced logoff of agent %s(%s). Agent no longer configured.\n",
918 agent->username, ast_channel_name(logged));
919 ast_softhangup(logged, AST_SOFTHANGUP_EXPLICIT);
920 ast_channel_unref(logged);
922 ao2_iterator_destroy(iter);
925 static void agents_post_apply_config(void)
927 struct ao2_iterator iter;
928 struct agent_cfg *cfg;
929 RAII_VAR(struct agents_cfg *, cfgs, ao2_global_obj_ref(cfg_handle), ao2_cleanup);
931 ast_assert(cfgs != NULL);
934 iter = ao2_iterator_init(cfgs->agents, 0);
935 for (; (cfg = ao2_iterator_next(&iter)); ao2_ref(cfg, -1)) {
936 RAII_VAR(struct agent_pvt *, agent, ao2_find(agents, cfg->username, OBJ_KEY), ao2_cleanup);
941 if (!agent->logged) {
942 struct agent_cfg *cfg_old;
944 /* Replace the config of agents not logged in. */
945 cfg_old = agent->cfg;
948 ao2_cleanup(cfg_old);
953 agent = agent_pvt_new(cfg);
957 ao2_link(agents, agent);
958 ast_debug(1, "Agent %s: Created.\n", agent->username);
959 agent_devstate_changed(agent->username);
961 ao2_iterator_destroy(&iter);
965 static int agent_logoff_request(const char *agent_id, int soft)
967 struct ast_channel *logged;
968 RAII_VAR(struct agent_pvt *, agent, ao2_find(agents, agent_id, OBJ_KEY), ao2_cleanup);
975 logged = agent_lock_logged(agent);
978 agent->deferred_logoff = 1;
980 ast_softhangup(logged, AST_SOFTHANGUP_EXPLICIT);
982 ast_channel_unlock(logged);
983 ast_channel_unref(logged);
989 /*! Agent holding bridge instance holder. */
990 static AO2_GLOBAL_OBJ_STATIC(agent_holding);
992 /*! Agent holding bridge deferred creation lock. */
993 AST_MUTEX_DEFINE_STATIC(agent_holding_lock);
997 * \brief Connect the agent with the waiting caller.
1000 * \param bridge_channel Agent channel connecting to the caller.
1001 * \param agent Which agent is connecting to the caller.
1003 * \note The agent is locked on entry and not locked on exit.
1007 static void agent_connect_caller(struct ast_bridge_channel *bridge_channel, struct agent_pvt *agent)
1009 struct ast_bridge *caller_bridge;
1010 int record_agent_calls;
1013 record_agent_calls = agent->cfg->record_agent_calls;
1014 caller_bridge = agent->caller_bridge;
1015 agent->caller_bridge = NULL;
1016 agent->state = AGENT_STATE_ON_CALL;
1017 time(&agent->call_start);
1018 agent_unlock(agent);
1020 if (!caller_bridge) {
1022 ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END);
1025 res = ast_bridge_move(caller_bridge, bridge_channel->bridge, bridge_channel->chan,
1029 ast_bridge_destroy(caller_bridge);
1030 ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END);
1033 ast_bridge_channel_write_control_data(bridge_channel, AST_CONTROL_ANSWER, NULL, 0);
1035 if (record_agent_calls) {
1036 struct ast_bridge_features_automixmonitor options = {
1037 .start_stop = AUTO_MONITOR_START,
1041 * The agent is in the new bridge so we can invoke the
1042 * mixmonitor hook to only start recording.
1044 ast_bridge_features_do(AST_BRIDGE_BUILTIN_AUTOMIXMON, bridge_channel, &options);
1048 static int bridge_agent_hold_ack(struct ast_bridge_channel *bridge_channel, void *hook_pvt)
1050 struct agent_pvt *agent = hook_pvt;
1053 switch (agent->state) {
1054 case AGENT_STATE_CALL_WAIT_ACK:
1055 /* Connect to caller now. */
1056 ast_debug(1, "Agent %s: Acked call.\n", agent->username);
1057 agent_connect_caller(bridge_channel, agent);/* Will unlock agent. */
1062 agent_unlock(agent);
1066 static int bridge_agent_hold_heartbeat(struct ast_bridge_channel *bridge_channel, void *hook_pvt)
1068 struct agent_pvt *agent = hook_pvt;
1069 int probation_timedout = 0;
1070 int ack_timedout = 0;
1071 int wrapup_timedout = 0;
1072 int deferred_logoff;
1073 unsigned int wrapup_time;
1074 unsigned int auto_logoff;
1077 deferred_logoff = agent->deferred_logoff;
1078 if (deferred_logoff) {
1079 agent->state = AGENT_STATE_LOGGING_OUT;
1082 switch (agent->state) {
1083 case AGENT_STATE_PROBATION_WAIT:
1084 probation_timedout =
1085 LOGIN_WAIT_TIMEOUT_TIME <= (time(NULL) - agent->probation_start);
1086 if (probation_timedout) {
1087 /* Now ready for a caller. */
1088 agent->state = AGENT_STATE_READY_FOR_CALL;
1089 agent->devstate = AST_DEVICE_NOT_INUSE;
1092 case AGENT_STATE_CALL_WAIT_ACK:
1093 /* Check ack call time. */
1094 auto_logoff = agent->cfg->auto_logoff;
1095 if (ast_test_flag(agent, AGENT_FLAG_AUTO_LOGOFF)) {
1096 auto_logoff = agent->override_auto_logoff;
1099 auto_logoff *= 1000;
1100 ack_timedout = ast_tvdiff_ms(ast_tvnow(), agent->ack_time) > auto_logoff;
1102 agent->state = AGENT_STATE_LOGGING_OUT;
1106 case AGENT_STATE_CALL_WRAPUP:
1107 /* Check wrapup time. */
1108 wrapup_time = agent->cfg->wrapup_time;
1109 if (ast_test_flag(agent, AGENT_FLAG_WRAPUP_TIME)) {
1110 wrapup_time = agent->override_wrapup_time;
1112 wrapup_timedout = ast_tvdiff_ms(ast_tvnow(), agent->last_disconnect) > wrapup_time;
1113 if (wrapup_timedout) {
1114 agent->state = AGENT_STATE_READY_FOR_CALL;
1115 agent->devstate = AST_DEVICE_NOT_INUSE;
1121 agent_unlock(agent);
1123 if (deferred_logoff) {
1124 ast_debug(1, "Agent %s: Deferred logoff.\n", agent->username);
1125 ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END);
1126 } else if (probation_timedout) {
1127 ast_debug(1, "Agent %s: Login complete.\n", agent->username);
1128 agent_devstate_changed(agent->username);
1129 } else if (ack_timedout) {
1130 ast_debug(1, "Agent %s: Ack call timeout.\n", agent->username);
1131 ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END);
1132 } else if (wrapup_timedout) {
1133 ast_debug(1, "Agent %s: Wrapup timeout. Ready for new call.\n", agent->username);
1134 agent_devstate_changed(agent->username);
1140 static void agent_after_bridge_cb(struct ast_channel *chan, void *data);
1141 static void agent_after_bridge_cb_failed(enum ast_bridge_after_cb_reason reason, void *data);
1145 * \brief ast_bridge agent_hold push method.
1148 * \param self Bridge to operate upon.
1149 * \param bridge_channel Bridge channel to push.
1150 * \param swap Bridge channel to swap places with if not NULL.
1152 * \note On entry, self is already locked.
1154 * \retval 0 on success
1155 * \retval -1 on failure
1157 static int bridge_agent_hold_push(struct ast_bridge *self, struct ast_bridge_channel *bridge_channel, struct ast_bridge_channel *swap)
1160 unsigned int wrapup_time;
1161 char dtmf[AST_FEATURE_MAX_LEN];
1162 struct ast_channel *chan;
1163 const char *moh_class;
1164 RAII_VAR(struct agent_pvt *, agent, NULL, ao2_cleanup);
1166 chan = bridge_channel->chan;
1168 agent = ao2_find(agents, swap ? swap->chan : chan, 0);
1170 /* Could not find the agent. */
1174 /* Setup agent entertainment */
1176 moh_class = ast_strdupa(agent->cfg->moh);
1177 agent_unlock(agent);
1178 res |= ast_channel_add_bridge_role(chan, "holding_participant");
1179 res |= ast_channel_set_bridge_role_option(chan, "holding_participant", "idle_mode", "musiconhold");
1180 res |= ast_channel_set_bridge_role_option(chan, "holding_participant", "moh_class", moh_class);
1182 /* Add DTMF acknowledge hook. */
1185 if (ast_test_flag(agent, AGENT_FLAG_ACK_CALL)
1186 ? agent->override_ack_call : agent->cfg->ack_call) {
1187 const char *dtmf_accept;
1189 dtmf_accept = ast_test_flag(agent, AGENT_FLAG_DTMF_ACCEPT)
1190 ? agent->override_dtmf_accept : agent->cfg->dtmf_accept;
1191 ast_copy_string(dtmf, dtmf_accept, sizeof(dtmf));
1193 agent_unlock(agent);
1194 if (!ast_strlen_zero(dtmf)) {
1196 if (ast_bridge_dtmf_hook(bridge_channel->features, dtmf, bridge_agent_hold_ack,
1197 agent, __ao2_cleanup, AST_BRIDGE_HOOK_REMOVE_ON_PULL)) {
1203 /* Add heartbeat interval hook. */
1205 if (ast_bridge_interval_hook(bridge_channel->features, 0, 1000,
1206 bridge_agent_hold_heartbeat, agent, __ao2_cleanup, AST_BRIDGE_HOOK_REMOVE_ON_PULL)) {
1211 res |= ast_bridge_base_v_table.push(self, bridge_channel, swap);
1213 ast_channel_remove_bridge_role(chan, "holding_participant");
1218 res = ast_bridge_set_after_callback(chan, agent_after_bridge_cb,
1219 agent_after_bridge_cb_failed, chan);
1221 ast_channel_remove_bridge_role(chan, "holding_participant");
1226 ast_channel_unref(agent->logged);
1227 agent->logged = ast_channel_ref(chan);
1228 agent_unlock(agent);
1231 * Kick the channel out so it can come back in fully controlled.
1232 * Otherwise, the after bridge callback will linger and the
1233 * agent will have some slightly different behavior in corner
1236 ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END);
1241 switch (agent->state) {
1242 case AGENT_STATE_LOGGED_OUT:
1244 * \todo XXX the login probation time should be only if it is needed.
1246 * Need to determine if there are any local channels that can
1247 * optimize and wait until they actually do before leaving the
1248 * AGENT_STATE_PROBATION_WAIT state. For now, the blind
1249 * timer of LOGIN_WAIT_TIMEOUT_TIME will do.
1252 * Start the login probation timer.
1254 * We cannot handle an agent local channel optimization when the
1255 * agent is on a call. The optimization may kick the agent
1256 * channel we know about out of the call without our being able
1257 * to switch to the replacement channel. Get any agent local
1258 * channel optimization out of the way while the agent is in the
1261 time(&agent->probation_start);
1262 agent->state = AGENT_STATE_PROBATION_WAIT;
1263 agent_unlock(agent);
1265 case AGENT_STATE_PROBATION_WAIT:
1266 /* Restart the probation timer. */
1267 time(&agent->probation_start);
1268 agent_unlock(agent);
1270 case AGENT_STATE_READY_FOR_CALL:
1272 * Likely someone manually kicked us out of the holding bridge
1273 * and we came right back in.
1275 agent_unlock(agent);
1278 /* Unexpected agent state. */
1281 case AGENT_STATE_CALL_PRESENT:
1282 case AGENT_STATE_CALL_WAIT_ACK:
1283 agent->state = AGENT_STATE_READY_FOR_CALL;
1284 agent->devstate = AST_DEVICE_NOT_INUSE;
1285 agent_unlock(agent);
1286 ast_debug(1, "Agent %s: Call abort recovery complete.\n", agent->username);
1287 agent_devstate_changed(agent->username);
1289 case AGENT_STATE_ON_CALL:
1290 case AGENT_STATE_CALL_WRAPUP:
1291 wrapup_time = agent->cfg->wrapup_time;
1292 if (ast_test_flag(agent, AGENT_FLAG_WRAPUP_TIME)) {
1293 wrapup_time = agent->override_wrapup_time;
1296 agent->state = AGENT_STATE_CALL_WRAPUP;
1298 agent->state = AGENT_STATE_READY_FOR_CALL;
1299 agent->devstate = AST_DEVICE_NOT_INUSE;
1301 agent_unlock(agent);
1303 /* No wrapup time. */
1304 ast_debug(1, "Agent %s: Ready for new call.\n", agent->username);
1305 agent_devstate_changed(agent->username);
1315 * \brief ast_bridge agent_hold pull method.
1317 * \param self Bridge to operate upon.
1318 * \param bridge_channel Bridge channel to pull.
1321 * Remove any channel hooks controlled by the bridge. Release
1322 * any resources held by bridge_channel->bridge_pvt and release
1323 * bridge_channel->bridge_pvt.
1325 * \note On entry, self is already locked.
1329 static void bridge_agent_hold_pull(struct ast_bridge *self, struct ast_bridge_channel *bridge_channel)
1331 ast_channel_remove_bridge_role(bridge_channel->chan, "holding_participant");
1332 ast_bridge_base_v_table.pull(self, bridge_channel);
1336 * \brief The bridge is being dissolved.
1338 * \param self Bridge to operate upon.
1341 * The bridge is being dissolved. Remove any external
1342 * references to the bridge so it can be destroyed.
1344 * \note On entry, self must NOT be locked.
1348 static void bridge_agent_hold_dissolving(struct ast_bridge *self)
1350 ao2_global_obj_replace_unref(agent_holding, NULL);
1351 ast_bridge_base_v_table.dissolving(self);
1354 static struct ast_bridge_methods bridge_agent_hold_v_table;
1356 static struct ast_bridge *bridge_agent_hold_new(void)
1358 struct ast_bridge *bridge;
1360 bridge = bridge_alloc(sizeof(struct ast_bridge), &bridge_agent_hold_v_table);
1361 bridge = bridge_base_init(bridge, AST_BRIDGE_CAPABILITY_HOLDING,
1362 AST_BRIDGE_FLAG_MERGE_INHIBIT_TO | AST_BRIDGE_FLAG_MERGE_INHIBIT_FROM
1363 | AST_BRIDGE_FLAG_SWAP_INHIBIT_FROM | AST_BRIDGE_FLAG_TRANSFER_PROHIBITED);
1364 bridge = bridge_register(bridge);
1368 static void bridge_init_agent_hold(void)
1370 /* Setup bridge agent_hold subclass v_table. */
1371 bridge_agent_hold_v_table = ast_bridge_base_v_table;
1372 bridge_agent_hold_v_table.name = "agent_hold";
1373 bridge_agent_hold_v_table.dissolving = bridge_agent_hold_dissolving;
1374 bridge_agent_hold_v_table.push = bridge_agent_hold_push;
1375 bridge_agent_hold_v_table.pull = bridge_agent_hold_pull;
1378 static int bridge_agent_hold_deferred_create(void)
1380 RAII_VAR(struct ast_bridge *, holding, ao2_global_obj_ref(agent_holding), ao2_cleanup);
1383 ast_mutex_lock(&agent_holding_lock);
1384 holding = ao2_global_obj_ref(agent_holding);
1386 holding = bridge_agent_hold_new();
1387 ao2_global_obj_replace_unref(agent_holding, holding);
1389 ast_mutex_unlock(&agent_holding_lock);
1391 ast_log(LOG_ERROR, "Could not create agent holding bridge.\n");
1398 static void send_agent_login(struct ast_channel *chan, const char *agent)
1400 RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
1402 ast_assert(agent != NULL);
1404 blob = ast_json_pack("{s: s}",
1410 ast_channel_publish_blob(chan, ast_channel_agent_login_type(), blob);
1413 static void send_agent_logoff(struct ast_channel *chan, const char *agent, long logintime)
1415 RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
1417 ast_assert(agent != NULL);
1419 blob = ast_json_pack("{s: s, s: i}",
1421 "logintime", logintime);
1426 ast_channel_publish_blob(chan, ast_channel_agent_logoff_type(), blob);
1431 * \brief Logout the agent.
1434 * \param agent Which agent logging out.
1436 * \note On entry agent is already locked. On exit it is no longer locked.
1440 static void agent_logout(struct agent_pvt *agent)
1442 struct ast_channel *logged;
1443 struct ast_bridge *caller_bridge;
1444 long time_logged_in;
1446 time_logged_in = time(NULL) - agent->login_start;
1447 logged = agent->logged;
1448 agent->logged = NULL;
1449 caller_bridge = agent->caller_bridge;
1450 agent->caller_bridge = NULL;
1451 agent->state = AGENT_STATE_LOGGED_OUT;
1452 agent->devstate = AST_DEVICE_UNAVAILABLE;
1453 ast_clear_flag(agent, AST_FLAGS_ALL);
1454 agent_unlock(agent);
1455 agent_devstate_changed(agent->username);
1457 if (caller_bridge) {
1458 ast_bridge_destroy(caller_bridge);
1461 send_agent_logoff(logged, agent->username, time_logged_in);
1462 ast_verb(2, "Agent '%s' logged out. Logged in for %ld seconds.\n",
1463 agent->username, time_logged_in);
1464 ast_channel_unref(logged);
1469 * \brief Agent driver loop.
1472 * \param agent Which agent.
1473 * \param logged The logged in channel.
1477 static void agent_run(struct agent_pvt *agent, struct ast_channel *logged)
1479 struct ast_bridge_features features;
1481 if (ast_bridge_features_init(&features)) {
1482 goto agent_run_cleanup;
1485 struct agents_cfg *cfgs;
1486 struct agent_cfg *cfg_new;
1487 struct agent_cfg *cfg_old;
1488 struct ast_bridge *holding;
1489 struct ast_bridge *caller_bridge;
1491 holding = ao2_global_obj_ref(agent_holding);
1493 ast_debug(1, "Agent %s: Someone destroyed the agent holding bridge.\n",
1499 * When the agent channel leaves the bridging system we usually
1500 * want to put the agent back into the holding bridge for the
1503 ast_bridge_join(holding, logged, NULL, &features, NULL, 1);
1504 if (logged != agent->logged) {
1505 /* This channel is no longer the logged in agent. */
1510 /* The agent is no longer configured. */
1514 /* Update the agent's config before rejoining the holding bridge. */
1515 cfgs = ao2_global_obj_ref(cfg_handle);
1517 /* There is no agent configuration. All agents were destroyed. */
1520 cfg_new = ao2_find(cfgs->agents, agent->username, OBJ_KEY);
1523 /* The agent is no longer configured. */
1527 cfg_old = agent->cfg;
1528 agent->cfg = cfg_new;
1530 agent->last_disconnect = ast_tvnow();
1532 /* Clear out any caller bridge before rejoining the holding bridge. */
1533 caller_bridge = agent->caller_bridge;
1534 agent->caller_bridge = NULL;
1535 agent_unlock(agent);
1536 ao2_ref(cfg_old, -1);
1537 if (caller_bridge) {
1538 ast_bridge_destroy(caller_bridge);
1541 if (agent->state == AGENT_STATE_LOGGING_OUT
1542 || agent->deferred_logoff
1543 || ast_check_hangup_locked(logged)) {
1544 /* The agent was requested to logout or hungup. */
1549 * It is safe to access agent->waiting_colp without a lock. It
1550 * is only setup on agent login and not changed.
1552 ast_channel_update_connected_line(logged, &agent->waiting_colp, NULL);
1554 ast_bridge_features_cleanup(&features);
1558 if (logged != agent->logged) {
1560 * We are no longer the agent channel because of local channel
1563 agent_unlock(agent);
1564 ast_debug(1, "Agent %s: Channel %s is no longer the agent.\n",
1565 agent->username, ast_channel_name(logged));
1568 agent_logout(agent);
1571 static void agent_after_bridge_cb(struct ast_channel *chan, void *data)
1573 struct agent_pvt *agent;
1575 agent = ao2_find(agents, chan, 0);
1580 ast_debug(1, "Agent %s: New agent channel %s.\n",
1581 agent->username, ast_channel_name(chan));
1582 agent_run(agent, chan);
1586 static void agent_after_bridge_cb_failed(enum ast_bridge_after_cb_reason reason, void *data)
1588 struct ast_channel *chan = data;
1589 struct agent_pvt *agent;
1591 agent = ao2_find(agents, chan, 0);
1595 ast_log(LOG_WARNING, "Agent %s: Forced logout. Lost control of %s because: %s\n",
1596 agent->username, ast_channel_name(chan),
1597 ast_bridge_after_cb_reason_string(reason));
1599 agent_logout(agent);
1605 * \brief Get the lock on the agent bridge_channel and return it.
1608 * \param agent Whose bridge_channel to get.
1610 * \retval bridge_channel on success (Reffed and locked).
1611 * \retval NULL on error.
1613 static struct ast_bridge_channel *agent_bridge_channel_get_lock(struct agent_pvt *agent)
1615 struct ast_channel *logged;
1616 struct ast_bridge_channel *bc;
1620 logged = agent->logged;
1622 agent_unlock(agent);
1625 ast_channel_ref(logged);
1626 agent_unlock(agent);
1628 ast_channel_lock(logged);
1629 bc = ast_channel_get_bridge_channel(logged);
1630 ast_channel_unlock(logged);
1631 ast_channel_unref(logged);
1633 if (agent->logged != logged) {
1639 ast_bridge_channel_lock(bc);
1640 if (bc->chan != logged || agent->logged != logged) {
1641 ast_bridge_channel_unlock(bc);
1649 static void caller_abort_agent(struct agent_pvt *agent)
1651 struct ast_bridge_channel *logged;
1653 logged = agent_bridge_channel_get_lock(agent);
1655 struct ast_bridge *caller_bridge;
1657 ast_debug(1, "Agent '%s' no longer logged in.\n", agent->username);
1660 caller_bridge = agent->caller_bridge;
1661 agent->caller_bridge = NULL;
1662 agent_unlock(agent);
1663 if (caller_bridge) {
1664 ast_bridge_destroy(caller_bridge);
1669 /* Kick the agent out of the holding bridge to reset it. */
1670 ast_bridge_channel_leave_bridge_nolock(logged, BRIDGE_CHANNEL_STATE_END);
1671 ast_bridge_channel_unlock(logged);
1674 static int caller_safety_timeout(struct ast_bridge_channel *bridge_channel, void *hook_pvt)
1676 struct agent_pvt *agent = hook_pvt;
1678 if (agent->state == AGENT_STATE_CALL_PRESENT) {
1679 ast_verb(3, "Agent '%s' did not respond. Safety timeout.\n", agent->username);
1680 ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END);
1681 caller_abort_agent(agent);
1687 static void agent_alert(struct ast_bridge_channel *bridge_channel, const void *payload, size_t payload_size)
1689 const char *agent_id = payload;
1690 const char *playfile;
1691 RAII_VAR(struct agent_pvt *, agent, NULL, ao2_cleanup);
1693 agent = ao2_find(agents, agent_id, OBJ_KEY);
1695 ast_debug(1, "Agent '%s' does not exist. Where did it go?\n", agent_id);
1699 /* Change holding bridge participant role's idle mode to silence */
1700 ast_bridge_channel_lock_bridge(bridge_channel);
1701 ast_bridge_channel_clear_roles(bridge_channel);
1702 ast_channel_set_bridge_role_option(bridge_channel->chan, "holding_participant", "idle_mode", "silence");
1703 ast_bridge_channel_establish_roles(bridge_channel);
1704 ast_bridge_unlock(bridge_channel->bridge);
1706 /* Alert the agent. */
1708 playfile = ast_strdupa(agent->cfg->beep_sound);
1709 agent_unlock(agent);
1710 ast_stream_and_wait(bridge_channel->chan, playfile, AST_DIGIT_NONE);
1713 switch (agent->state) {
1714 case AGENT_STATE_CALL_PRESENT:
1715 if (ast_test_flag(agent, AGENT_FLAG_ACK_CALL)
1716 ? agent->override_ack_call : agent->cfg->ack_call) {
1717 agent->state = AGENT_STATE_CALL_WAIT_ACK;
1718 agent->ack_time = ast_tvnow();
1722 /* Connect to caller now. */
1723 ast_debug(1, "Agent %s: Immediately connecting to call.\n", agent->username);
1724 agent_connect_caller(bridge_channel, agent);/* Will unlock agent. */
1729 agent_unlock(agent);
1732 static int send_alert_to_agent(struct ast_bridge_channel *bridge_channel, const char *agent_id)
1734 return ast_bridge_channel_queue_callback(bridge_channel,
1735 AST_BRIDGE_CHANNEL_CB_OPTION_MEDIA, agent_alert, agent_id, strlen(agent_id) + 1);
1738 static int send_colp_to_agent(struct ast_bridge_channel *bridge_channel, struct ast_party_connected_line *connected)
1740 struct ast_set_party_connected_line update = {
1745 unsigned char data[1024]; /* This should be large enough */
1748 datalen = ast_connected_line_build_data(data, sizeof(data), connected, &update);
1749 if (datalen == (size_t) -1) {
1753 return ast_bridge_channel_queue_control_data(bridge_channel,
1754 AST_CONTROL_CONNECTED_LINE, data, datalen);
1758 * \brief Dialplan AgentRequest application to locate an agent to talk with.
1760 * \param chan Channel wanting to talk with an agent.
1761 * \param data Application parameters
1763 * \retval 0 To continue in dialplan.
1764 * \retval -1 To hangup.
1766 static int agent_request_exec(struct ast_channel *chan, const char *data)
1768 struct ast_bridge *caller_bridge;
1769 struct ast_bridge_channel *logged;
1772 struct ast_bridge_features caller_features;
1773 struct ast_party_connected_line connected;
1774 AST_DECLARE_APP_ARGS(args,
1775 AST_APP_ARG(agent_id);
1776 AST_APP_ARG(other); /* Any remaining unused arguments */
1779 RAII_VAR(struct agent_pvt *, agent, NULL, ao2_cleanup);
1781 if (bridge_agent_hold_deferred_create()) {
1785 parse = ast_strdupa(data);
1786 AST_STANDARD_APP_ARGS(args, parse);
1788 if (ast_strlen_zero(args.agent_id)) {
1789 ast_log(LOG_WARNING, "AgentRequest requires an AgentId\n");
1793 /* Find the agent. */
1794 agent = ao2_find(agents, args.agent_id, OBJ_KEY);
1796 ast_verb(3, "Agent '%s' does not exist.\n", args.agent_id);
1797 pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "INVALID");
1801 if (ast_bridge_features_init(&caller_features)) {
1805 /* Add safety timeout hook. */
1807 if (ast_bridge_interval_hook(&caller_features, 0, CALLER_SAFETY_TIMEOUT_TIME,
1808 caller_safety_timeout, agent, __ao2_cleanup, AST_BRIDGE_HOOK_REMOVE_ON_PULL)) {
1810 ast_bridge_features_cleanup(&caller_features);
1814 caller_bridge = ast_bridge_basic_new();
1815 if (!caller_bridge) {
1816 ast_bridge_features_cleanup(&caller_features);
1820 /* Get COLP for agent. */
1821 ast_party_connected_line_init(&connected);
1822 ast_channel_lock(chan);
1823 ast_connected_line_copy_from_caller(&connected, ast_channel_caller(chan));
1824 ast_channel_unlock(chan);
1827 switch (agent->state) {
1828 case AGENT_STATE_LOGGED_OUT:
1829 case AGENT_STATE_LOGGING_OUT:
1830 agent_unlock(agent);
1831 ast_party_connected_line_free(&connected);
1832 ast_bridge_destroy(caller_bridge);
1833 ast_bridge_features_cleanup(&caller_features);
1834 ast_verb(3, "Agent '%s' not logged in.\n", agent->username);
1835 pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "NOT_LOGGED_IN");
1837 case AGENT_STATE_READY_FOR_CALL:
1838 ao2_ref(caller_bridge, +1);
1839 agent->caller_bridge = caller_bridge;
1840 agent->state = AGENT_STATE_CALL_PRESENT;
1841 agent->devstate = AST_DEVICE_INUSE;
1844 agent_unlock(agent);
1845 ast_party_connected_line_free(&connected);
1846 ast_bridge_destroy(caller_bridge);
1847 ast_bridge_features_cleanup(&caller_features);
1848 ast_verb(3, "Agent '%s' is busy.\n", agent->username);
1849 pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "BUSY");
1852 agent_unlock(agent);
1853 agent_devstate_changed(agent->username);
1855 logged = agent_bridge_channel_get_lock(agent);
1857 ast_party_connected_line_free(&connected);
1858 ast_bridge_destroy(caller_bridge);
1859 ast_bridge_features_cleanup(&caller_features);
1860 ast_verb(3, "Agent '%s' not logged in.\n", agent->username);
1861 pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "NOT_LOGGED_IN");
1862 caller_abort_agent(agent);
1866 send_colp_to_agent(logged, &connected);
1867 ast_party_connected_line_free(&connected);
1869 res = send_alert_to_agent(logged, agent->username);
1870 ast_bridge_channel_unlock(logged);
1871 ao2_ref(logged, -1);
1873 ast_bridge_destroy(caller_bridge);
1874 ast_bridge_features_cleanup(&caller_features);
1875 ast_verb(3, "Agent '%s': Failed to alert the agent.\n", agent->username);
1876 pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "ERROR");
1877 caller_abort_agent(agent);
1881 ast_indicate(chan, AST_CONTROL_RINGING);
1882 ast_bridge_join(caller_bridge, chan, NULL, &caller_features, NULL, 1);
1883 ast_bridge_features_cleanup(&caller_features);
1890 * \brief Get agent config values from the login channel.
1893 * \param agent What to setup channel config values on.
1894 * \param chan Channel logging in as an agent.
1898 static void agent_login_channel_config(struct agent_pvt *agent, struct ast_channel *chan)
1900 struct ast_flags opts = { 0 };
1901 struct ast_party_connected_line connected;
1902 unsigned int override_ack_call = 0;
1903 unsigned int override_auto_logoff = 0;
1904 unsigned int override_wrapup_time = 0;
1905 const char *override_dtmf_accept = NULL;
1908 ast_party_connected_line_init(&connected);
1910 /* Get config values from channel. */
1911 ast_channel_lock(chan);
1912 ast_party_connected_line_copy(&connected, ast_channel_connected(chan));
1914 var = pbx_builtin_getvar_helper(chan, "AGENTACKCALL");
1915 if (!ast_strlen_zero(var)) {
1916 override_ack_call = ast_true(var) ? 1 : 0;
1917 ast_set_flag(&opts, AGENT_FLAG_ACK_CALL);
1920 var = pbx_builtin_getvar_helper(chan, "AGENTACCEPTDTMF");
1921 if (!ast_strlen_zero(var)) {
1922 override_dtmf_accept = ast_strdupa(var);
1923 ast_set_flag(&opts, AGENT_FLAG_DTMF_ACCEPT);
1926 var = pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF");
1927 if (!ast_strlen_zero(var)) {
1928 if (sscanf(var, "%u", &override_auto_logoff) == 1) {
1929 ast_set_flag(&opts, AGENT_FLAG_AUTO_LOGOFF);
1933 var = pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME");
1934 if (!ast_strlen_zero(var)) {
1935 if (sscanf(var, "%u", &override_wrapup_time) == 1) {
1936 ast_set_flag(&opts, AGENT_FLAG_WRAPUP_TIME);
1939 ast_channel_unlock(chan);
1941 /* Set config values on agent. */
1943 ast_party_connected_line_free(&agent->waiting_colp);
1944 agent->waiting_colp = connected;
1946 ast_string_field_set(agent, override_dtmf_accept, override_dtmf_accept);
1947 ast_copy_flags(agent, &opts, AST_FLAGS_ALL);
1948 agent->override_auto_logoff = override_auto_logoff;
1949 agent->override_wrapup_time = override_wrapup_time;
1950 agent->override_ack_call = override_ack_call;
1951 agent_unlock(agent);
1954 enum AGENT_LOGIN_OPT_FLAGS {
1955 OPT_SILENT = (1 << 0),
1957 AST_APP_OPTIONS(agent_login_opts, BEGIN_OPTIONS
1958 AST_APP_OPTION('s', OPT_SILENT),
1962 * \brief Dialplan AgentLogin application to log in an agent.
1964 * \param chan Channel attempting to login as an agent.
1965 * \param data Application parameters
1967 * \retval 0 To continue in dialplan.
1968 * \retval -1 To hangup.
1970 static int agent_login_exec(struct ast_channel *chan, const char *data)
1973 struct ast_flags opts;
1974 AST_DECLARE_APP_ARGS(args,
1975 AST_APP_ARG(agent_id);
1976 AST_APP_ARG(options);
1977 AST_APP_ARG(other); /* Any remaining unused arguments */
1980 RAII_VAR(struct agent_pvt *, agent, NULL, ao2_cleanup);
1982 if (bridge_agent_hold_deferred_create()) {
1986 if (ast_channel_state(chan) != AST_STATE_UP && ast_answer(chan)) {
1990 parse = ast_strdupa(data);
1991 AST_STANDARD_APP_ARGS(args, parse);
1993 if (ast_strlen_zero(args.agent_id)) {
1994 ast_log(LOG_WARNING, "AgentLogin requires an AgentId\n");
1998 if (ast_app_parse_options(agent_login_opts, &opts, NULL, args.options)) {
1999 /* General invalid option syntax. */
2003 /* Find the agent. */
2004 agent = ao2_find(agents, args.agent_id, OBJ_KEY);
2006 ast_verb(3, "Agent '%s' does not exist.\n", args.agent_id);
2007 pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "INVALID");
2011 /* Has someone already logged in as this agent already? */
2013 if (agent->logged) {
2014 agent_unlock(agent);
2015 ast_verb(3, "Agent '%s' already logged in.\n", agent->username);
2016 pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "ALREADY_LOGGED_IN");
2019 agent->logged = ast_channel_ref(chan);
2020 agent->last_disconnect = ast_tvnow();
2021 time(&agent->login_start);
2022 agent_unlock(agent);
2024 agent_login_channel_config(agent, chan);
2026 if (!ast_test_flag(&opts, OPT_SILENT)
2027 && !ast_streamfile(chan, "agent-loginok", ast_channel_language(chan))) {
2028 ast_waitstream(chan, "");
2031 ast_verb(2, "Agent '%s' logged in (format %s/%s)\n", agent->username,
2032 ast_getformatname(ast_channel_readformat(chan)),
2033 ast_getformatname(ast_channel_writeformat(chan)));
2034 send_agent_login(chan, agent->username);
2036 agent_run(agent, chan);
2040 static int agent_function_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
2043 struct agent_pvt *agent;
2044 struct ast_channel *logged;
2045 AST_DECLARE_APP_ARGS(args,
2046 AST_APP_ARG(agentid);
2052 parse = ast_strdupa(data ?: "");
2053 AST_NONSTANDARD_APP_ARGS(args, parse, ':');
2055 if (ast_strlen_zero(args.agentid)) {
2056 ast_log(LOG_WARNING, "The AGENT function requires an argument - agentid!\n");
2060 args.item = "status";
2063 agent = ao2_find(agents, args.agentid, OBJ_KEY);
2065 ast_log(LOG_WARNING, "Agent '%s' not found!\n", args.agentid);
2070 if (!strcasecmp(args.item, "status")) {
2073 if (agent->logged) {
2074 status = "LOGGEDIN";
2076 status = "LOGGEDOUT";
2078 ast_copy_string(buf, status, len);
2079 } else if (!strcasecmp(args.item, "name")) {
2080 ast_copy_string(buf, agent->cfg->full_name, len);
2081 } else if (!strcasecmp(args.item, "mohclass")) {
2082 ast_copy_string(buf, agent->cfg->moh, len);
2083 } else if (!strcasecmp(args.item, "channel")) {
2084 logged = agent_lock_logged(agent);
2088 ast_copy_string(buf, ast_channel_name(logged), len);
2089 ast_channel_unlock(logged);
2090 ast_channel_unref(logged);
2092 pos = strrchr(buf, '-');
2097 } else if (!strcasecmp(args.item, "fullchannel")) {
2098 logged = agent_lock_logged(agent);
2100 ast_copy_string(buf, ast_channel_name(logged), len);
2101 ast_channel_unlock(logged);
2102 ast_channel_unref(logged);
2105 agent_unlock(agent);
2111 static struct ast_custom_function agent_function = {
2113 .read = agent_function_read,
2116 struct agent_complete {
2117 /*! Nth match to return. */
2119 /*! Which match currently on. */
2123 static int complete_agent_search(void *obj, void *arg, void *data, int flags)
2125 struct agent_complete *search = data;
2127 if (++search->which > search->state) {
2133 static char *complete_agent(const char *word, int state)
2136 struct agent_pvt *agent;
2137 struct agent_complete search = {
2141 agent = ao2_callback_data(agents, ast_strlen_zero(word) ? 0 : OBJ_PARTIAL_KEY,
2142 complete_agent_search, (char *) word, &search);
2146 ret = ast_strdup(agent->username);
2151 static int complete_agent_logoff_search(void *obj, void *arg, void *data, int flags)
2153 struct agent_pvt *agent = obj;
2154 struct agent_complete *search = data;
2156 if (!agent->logged) {
2159 if (++search->which > search->state) {
2165 static char *complete_agent_logoff(const char *word, int state)
2168 struct agent_pvt *agent;
2169 struct agent_complete search = {
2173 agent = ao2_callback_data(agents, ast_strlen_zero(word) ? 0 : OBJ_PARTIAL_KEY,
2174 complete_agent_logoff_search, (char *) word, &search);
2178 ret = ast_strdup(agent->username);
2183 static void agent_show_requested(struct ast_cli_args *a, int online_only)
2185 #define FORMAT_HDR "%-8s %-20s %-11s %-30s %s\n"
2186 #define FORMAT_ROW "%-8s %-20s %-11s %-30s %s\n"
2188 struct ao2_iterator iter;
2189 struct agent_pvt *agent;
2190 struct ast_str *out = ast_str_alloca(512);
2191 unsigned int agents_total = 0;
2192 unsigned int agents_logged_in = 0;
2193 unsigned int agents_talking = 0;
2195 ast_cli(a->fd, FORMAT_HDR, "Agent-ID", "Name", "State", "Channel", "Talking with");
2196 iter = ao2_iterator_init(agents, 0);
2197 for (; (agent = ao2_iterator_next(&iter)); ao2_ref(agent, -1)) {
2198 struct ast_channel *logged;
2203 logged = agent_lock_logged(agent);
2205 const char *talking_with;
2209 talking_with = pbx_builtin_getvar_helper(logged, "BRIDGEPEER");
2210 if (!ast_strlen_zero(talking_with)) {
2215 ast_str_set(&out, 0, FORMAT_ROW, agent->username, agent->cfg->full_name,
2216 ast_devstate_str(agent->devstate), ast_channel_name(logged), talking_with);
2217 ast_channel_unlock(logged);
2218 ast_channel_unref(logged);
2220 ast_str_set(&out, 0, FORMAT_ROW, agent->username, agent->cfg->full_name,
2221 ast_devstate_str(agent->devstate), "", "");
2223 agent_unlock(agent);
2225 if (!online_only || logged) {
2226 ast_cli(a->fd, "%s", ast_str_buffer(out));
2229 ao2_iterator_destroy(&iter);
2231 ast_cli(a->fd, "\nDefined agents: %u, Logged in: %u, Talking: %u\n",
2232 agents_total, agents_logged_in, agents_talking);
2238 static char *agent_handle_show_online(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2242 e->command = "agent show online";
2244 "Usage: agent show online\n"
2245 " Provides summary information for logged in agents.\n";
2252 return CLI_SHOWUSAGE;
2255 agent_show_requested(a, 1);
2260 static char *agent_handle_show_all(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2264 e->command = "agent show all";
2266 "Usage: agent show all\n"
2267 " Provides summary information for all agents.\n";
2274 return CLI_SHOWUSAGE;
2277 agent_show_requested(a, 0);
2282 static char *agent_handle_show_specific(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2284 struct agent_pvt *agent;
2285 struct ast_channel *logged;
2286 struct ast_str *out = ast_str_alloca(4096);
2290 e->command = "agent show";
2292 "Usage: agent show <agent-id>\n"
2293 " Show information about the <agent-id> agent\n";
2297 return complete_agent(a->word, a->n);
2303 return CLI_SHOWUSAGE;
2306 agent = ao2_find(agents, a->argv[2], OBJ_KEY);
2308 ast_cli(a->fd, "Agent '%s' not found\n", a->argv[2]);
2313 logged = agent_lock_logged(agent);
2314 ast_str_set(&out, 0, "Id: %s\n", agent->username);
2315 ast_str_append(&out, 0, "Name: %s\n", agent->cfg->full_name);
2316 ast_str_append(&out, 0, "Beep: %s\n", agent->cfg->beep_sound);
2317 ast_str_append(&out, 0, "MOH: %s\n", agent->cfg->moh);
2318 ast_str_append(&out, 0, "RecordCalls: %s\n", AST_CLI_YESNO(agent->cfg->record_agent_calls));
2319 ast_str_append(&out, 0, "State: %s\n", ast_devstate_str(agent->devstate));
2321 const char *talking_with;
2323 ast_str_append(&out, 0, "LoggedInChannel: %s\n", ast_channel_name(logged));
2324 ast_str_append(&out, 0, "LoggedInTime: %ld\n", (long) agent->login_start);
2325 talking_with = pbx_builtin_getvar_helper(logged, "BRIDGEPEER");
2326 if (!ast_strlen_zero(talking_with)) {
2327 ast_str_append(&out, 0, "TalkingWith: %s\n", talking_with);
2328 ast_str_append(&out, 0, "CallStarted: %ld\n", (long) agent->call_start);
2330 ast_channel_unlock(logged);
2331 ast_channel_unref(logged);
2333 agent_unlock(agent);
2336 ast_cli(a->fd, "%s", ast_str_buffer(out));
2341 static char *agent_handle_logoff_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2345 e->command = "agent logoff";
2347 "Usage: agent logoff <agent-id> [soft]\n"
2348 " Sets an agent as no longer logged in.\n"
2349 " If 'soft' is specified, do not hangup existing calls.\n";
2353 return complete_agent_logoff(a->word, a->n);
2354 } else if (a->pos == 3 && a->n == 0
2355 && (ast_strlen_zero(a->word)
2356 || !strncasecmp("soft", a->word, strlen(a->word)))) {
2357 return ast_strdup("soft");
2362 if (a->argc < 3 || 4 < a->argc) {
2363 return CLI_SHOWUSAGE;
2365 if (a->argc == 4 && strcasecmp(a->argv[3], "soft")) {
2366 return CLI_SHOWUSAGE;
2369 if (!agent_logoff_request(a->argv[2], a->argc == 4)) {
2370 ast_cli(a->fd, "Logging out %s\n", a->argv[2]);
2376 static struct ast_cli_entry cli_agents[] = {
2377 AST_CLI_DEFINE(agent_handle_show_online, "Show status of online agents"),
2378 AST_CLI_DEFINE(agent_handle_show_all, "Show status of all agents"),
2379 AST_CLI_DEFINE(agent_handle_show_specific, "Show information about an agent"),
2380 AST_CLI_DEFINE(agent_handle_logoff_cmd, "Sets an agent offline"),
2383 static int action_agents(struct mansession *s, const struct message *m)
2385 const char *id = astman_get_header(m, "ActionID");
2386 char id_text[AST_MAX_BUF];
2387 struct ao2_iterator iter;
2388 struct agent_pvt *agent;
2389 struct ast_str *out = ast_str_alloca(4096);
2391 if (!ast_strlen_zero(id)) {
2392 snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", id);
2396 astman_send_ack(s, m, "Agents will follow");
2398 iter = ao2_iterator_init(agents, 0);
2399 for (; (agent = ao2_iterator_next(&iter)); ao2_ref(agent, -1)) {
2400 struct ast_channel *logged;
2403 logged = agent_lock_logged(agent);
2407 * AGENT_LOGGEDOFF - Agent isn't logged in
2408 * AGENT_IDLE - Agent is logged in, and waiting for call
2409 * AGENT_ONCALL - Agent is logged in, and on a call
2410 * AGENT_UNKNOWN - Don't know anything about agent. Shouldn't ever get this.
2412 ast_str_set(&out, 0, "Agent: %s\r\n", agent->username);
2413 ast_str_append(&out, 0, "Name: %s\r\n", agent->cfg->full_name);
2416 const char *talking_to_chan;
2417 struct ast_str *logged_headers;
2418 RAII_VAR(struct ast_channel_snapshot *, logged_snapshot, ast_channel_snapshot_create(logged), ao2_cleanup);
2420 if (!logged_snapshot
2421 || !(logged_headers =
2422 ast_manager_build_channel_state_string(logged_snapshot))) {
2423 ast_channel_unlock(logged);
2424 ast_channel_unref(logged);
2425 agent_unlock(agent);
2429 talking_to_chan = pbx_builtin_getvar_helper(logged, "BRIDGEPEER");
2430 if (!ast_strlen_zero(talking_to_chan)) {
2431 ast_str_append(&out, 0, "Status: %s\r\n", "AGENT_ONCALL");
2432 ast_str_append(&out, 0, "TalkingToChan: %s\r\n", talking_to_chan);
2433 ast_str_append(&out, 0, "CallStarted: %ld\n", (long) agent->call_start);
2435 ast_str_append(&out, 0, "Status: %s\r\n", "AGENT_IDLE");
2437 ast_str_append(&out, 0, "LoggedInTime: %ld\r\n", (long) agent->login_start);
2438 ast_str_append(&out, 0, "%s", ast_str_buffer(logged_headers));
2439 ast_channel_unlock(logged);
2440 ast_channel_unref(logged);
2441 ast_free(logged_headers);
2443 ast_str_append(&out, 0, "Status: %s\r\n", "AGENT_LOGGEDOFF");
2446 agent_unlock(agent);
2448 astman_append(s, "Event: Agents\r\n"
2450 ast_str_buffer(out), id_text);
2452 ao2_iterator_destroy(&iter);
2454 astman_append(s, "Event: AgentsComplete\r\n"
2460 static int action_agent_logoff(struct mansession *s, const struct message *m)
2462 const char *agent = astman_get_header(m, "Agent");
2463 const char *soft_s = astman_get_header(m, "Soft"); /* "true" is don't hangup */
2465 if (ast_strlen_zero(agent)) {
2466 astman_send_error(s, m, "No agent specified");
2470 if (!agent_logoff_request(agent, ast_true(soft_s))) {
2471 astman_send_ack(s, m, "Agent logged out");
2473 astman_send_error(s, m, "No such agent");
2479 static int unload_module(void)
2481 struct ast_bridge *holding;
2483 /* Unregister dialplan applications */
2484 ast_unregister_application(app_agent_login);
2485 ast_unregister_application(app_agent_request);
2487 /* Unregister dialplan functions */
2488 ast_custom_function_unregister(&agent_function);
2490 /* Unregister manager command */
2491 ast_manager_unregister("Agents");
2492 ast_manager_unregister("AgentLogoff");
2494 /* Unregister CLI commands */
2495 ast_cli_unregister_multiple(cli_agents, ARRAY_LEN(cli_agents));
2497 ast_devstate_prov_del("Agent");
2499 /* Destroy agent holding bridge. */
2500 holding = ao2_global_obj_replace(agent_holding, NULL);
2502 ast_bridge_destroy(holding);
2506 ao2_ref(agents, -1);
2511 static int load_module(void)
2515 agents = ao2_container_alloc_rbtree(AO2_ALLOC_OPT_LOCK_MUTEX,
2516 AO2_CONTAINER_ALLOC_OPT_DUPS_REPLACE, agent_pvt_sort_cmp, agent_pvt_cmp);
2518 return AST_MODULE_LOAD_FAILURE;
2520 if (load_config()) {
2521 ast_log(LOG_ERROR, "Unable to load config. Not loading module.\n");
2522 ao2_ref(agents, -1);
2524 return AST_MODULE_LOAD_DECLINE;
2527 /* Init agent holding bridge v_table. */
2528 bridge_init_agent_hold();
2530 /* Setup to provide Agent:agent-id device state. */
2531 res |= ast_devstate_prov_add("Agent", agent_pvt_devstate_get);
2534 res |= ast_cli_register_multiple(cli_agents, ARRAY_LEN(cli_agents));
2536 /* Manager commands */
2537 res |= ast_manager_register_xml("Agents", EVENT_FLAG_AGENT, action_agents);
2538 res |= ast_manager_register_xml("AgentLogoff", EVENT_FLAG_AGENT, action_agent_logoff);
2540 /* Dialplan Functions */
2541 res |= ast_custom_function_register(&agent_function);
2543 /* Dialplan applications */
2544 res |= ast_register_application_xml(app_agent_login, agent_login_exec);
2545 res |= ast_register_application_xml(app_agent_request, agent_request_exec);
2549 return AST_MODULE_LOAD_FAILURE;
2551 return AST_MODULE_LOAD_SUCCESS;
2554 static int reload(void)
2556 if (aco_process_config(&cfg_info, 1) == ACO_PROCESS_ERROR) {
2557 /* Just keep the config we already have in place. */
2563 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Call center agent pool applications",
2564 .load = load_module,
2565 .unload = unload_module,
2567 .load_pri = AST_MODPRI_DEVSTATE_PROVIDER,