2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 2013 Digium, Inc.
6 * Richard Mudgett <rmudgett@digium.com>
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
21 * \brief Call center agent pool.
23 * \author Richard Mudgett <rmudgett@digium.com>
26 * \arg \ref AstCREDITS
27 * \arg \ref Config_agent
30 <support_level>core</support_level>
36 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
38 #include "asterisk/cli.h"
39 #include "asterisk/app.h"
40 #include "asterisk/pbx.h"
41 #include "asterisk/module.h"
42 #include "asterisk/channel.h"
43 #include "asterisk/bridge.h"
44 #include "asterisk/bridge_internal.h"
45 #include "asterisk/bridge_basic.h"
46 #include "asterisk/bridge_after.h"
47 #include "asterisk/config_options.h"
48 #include "asterisk/features_config.h"
49 #include "asterisk/astobj2.h"
50 #include "asterisk/stringfields.h"
51 #include "asterisk/stasis_channels.h"
52 #include "asterisk/causes.h"
55 <application name="AgentLogin" language="en_US">
60 <parameter name="AgentId" required="true" />
61 <parameter name="options">
64 <para>silent login - do not announce the login ok segment after
65 agent logged on.</para>
71 <para>Login an agent to the system. Any agent authentication is assumed to
72 already be done by dialplan. While logged in, the agent can receive calls
73 and will hear the sound file specified by the config option custom_beep
74 when a new call comes in for the agent. Login failures will continue in
75 the dialplan with <variable>AGENT_STATUS</variable> set.</para>
76 <para>Before logging in, you can setup on the real agent channel the
77 CHANNEL(dtmf-features) an agent will have when talking to a caller
78 and you can setup on the channel running this application the
79 CONNECTEDLINE() information the agent will see while waiting for a
81 <para><variable>AGENT_STATUS</variable> enumeration values:</para>
83 <enum name = "INVALID"><para>The specified agent is invalid.</para></enum>
84 <enum name = "ALREADY_LOGGED_IN"><para>The agent is already logged in.</para></enum>
86 <note><para>The Agents:<replaceable>AgentId</replaceable> device state is
87 available to monitor the status of the agent.</para></note>
90 <ref type="application">Authenticate</ref>
91 <ref type="application">Queue</ref>
92 <ref type="application">AddQueueMember</ref>
93 <ref type="application">RemoveQueueMember</ref>
94 <ref type="application">PauseQueueMember</ref>
95 <ref type="application">UnpauseQueueMember</ref>
96 <ref type="function">AGENT</ref>
97 <ref type="function">CHANNEL(dtmf-features)</ref>
98 <ref type="function">CONNECTEDLINE()</ref>
99 <ref type="filename">agents.conf</ref>
100 <ref type="filename">queues.conf</ref>
103 <application name="AgentRequest" language="en_US">
105 Request an agent to connect with the channel.
108 <parameter name="AgentId" required="true" />
111 <para>Request an agent to connect with the channel. Failure to find and
112 alert an agent will continue in the dialplan with <variable>AGENT_STATUS</variable> set.</para>
113 <para><variable>AGENT_STATUS</variable> enumeration values:</para>
115 <enum name = "INVALID"><para>The specified agent is invalid.</para></enum>
116 <enum name = "NOT_LOGGED_IN"><para>The agent is not available.</para></enum>
117 <enum name = "BUSY"><para>The agent is on another call.</para></enum>
118 <enum name = "ERROR"><para>Alerting the agent failed.</para></enum>
122 <ref type="application">AgentLogin</ref>
125 <function name="AGENT" language="en_US">
127 Gets information about an Agent
130 <parameter name="AgentId" required="true" />
131 <parameter name="item">
132 <para>The valid items to retrieve are:</para>
135 <para>(default) The status of the agent (LOGGEDIN | LOGGEDOUT)</para>
137 <enum name="password">
138 <para>Deprecated. The dialplan handles any agent authentication.</para>
141 <para>The name of the agent</para>
143 <enum name="mohclass">
144 <para>MusicOnHold class</para>
146 <enum name="channel">
147 <para>The name of the active channel for the Agent (AgentLogin)</para>
149 <enum name="fullchannel">
150 <para>The untruncated name of the active channel for the Agent (AgentLogin)</para>
155 <description></description>
157 <manager name="Agents" language="en_US">
159 Lists agents and their status.
162 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
165 <para>Will list info about all defined agents.</para>
168 <ref type="managerEvent">Agents</ref>
169 <ref type="managerEvent">AgentsComplete</ref>
172 <managerEvent language="en_US" name="Agents">
173 <managerEventInstance class="EVENT_FLAG_AGENT">
175 Response event in a series to the Agents AMI action containing
176 information about a defined agent.
179 <parameter name="Agent">
180 <para>Agent ID of the agent.</para>
182 <parameter name="Name">
183 <para>User friendly name of the agent.</para>
185 <parameter name="Status">
186 <para>Current status of the agent.</para>
187 <para>The valid values are:</para>
189 <enum name="AGENT_LOGGEDOFF" />
190 <enum name="AGENT_IDLE" />
191 <enum name="AGENT_ONCALL" />
194 <parameter name="TalkingToChan">
195 <para><variable>BRIDGEPEER</variable> value on agent channel.</para>
196 <para>Present if Status value is <literal>AGENT_ONCALL</literal>.</para>
198 <parameter name="CallStarted">
199 <para>Epoche time when the agent started talking with the caller.</para>
200 <para>Present if Status value is <literal>AGENT_ONCALL</literal>.</para>
202 <parameter name="LoggedInTime">
203 <para>Epoche time when the agent logged in.</para>
204 <para>Present if Status value is <literal>AGENT_IDLE</literal> or <literal>AGENT_ONCALL</literal>.</para>
207 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
210 <para>The channel snapshot is present if the Status value is <literal>AGENT_IDLE</literal> or <literal>AGENT_ONCALL</literal>.</para>
213 <ref type="manager">Agents</ref>
215 </managerEventInstance>
217 <managerEvent language="en_US" name="AgentsComplete">
218 <managerEventInstance class="EVENT_FLAG_AGENT">
220 Final response event in a series of events to the Agents AMI action.
223 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
226 <ref type="manager">Agents</ref>
228 </managerEventInstance>
230 <manager name="AgentLogoff" language="en_US">
232 Sets an agent as no longer logged in.
235 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
236 <parameter name="Agent" required="true">
237 <para>Agent ID of the agent to log off.</para>
239 <parameter name="Soft">
240 <para>Set to <literal>true</literal> to not hangup existing calls.</para>
244 <para>Sets an agent as no longer logged in.</para>
247 <configInfo name="app_agent_pool" language="en_US">
248 <synopsis>Agent pool applications</synopsis>
250 <note><para>Option changes take effect on agent login or after an agent
251 disconnects from a call.</para></note>
253 <configFile name="agents.conf">
254 <configObject name="global">
255 <synopsis>Unused, but reserved.</synopsis>
257 <configObject name="agent-id">
258 <synopsis>Configure an agent for the pool.</synopsis>
260 <xi:include xpointer="xpointer(/docs/configInfo[@name='app_agent_pool']/description/note)" />
262 <configOption name="ackcall">
263 <synopsis>Enable to require the agent to acknowledge a call.</synopsis>
265 <para>Enable to require the agent to give a DTMF acknowledgement
266 when the agent receives a call.</para>
267 <note><para>The option is overridden by <variable>AGENTACKCALL</variable> on agent login.</para></note>
268 <xi:include xpointer="xpointer(/docs/configInfo[@name='app_agent_pool']/description/note)" />
271 <configOption name="acceptdtmf">
272 <synopsis>DTMF key sequence the agent uses to acknowledge a call.</synopsis>
274 <note><para>The option is overridden by <variable>AGENTACCEPTDTMF</variable> on agent login.</para></note>
275 <note><para>The option is ignored unless the ackcall option is enabled.</para></note>
276 <xi:include xpointer="xpointer(/docs/configInfo[@name='app_agent_pool']/description/note)" />
279 <configOption name="autologoff">
280 <synopsis>Time the agent has to acknowledge a call before being logged off.</synopsis>
282 <para>Set how many seconds a call for the agent has to wait for the
283 agent to acknowledge the call before the agent is automatically
284 logged off. If set to zero then the call will wait forever for
285 the agent to acknowledge.</para>
286 <note><para>The option is overridden by <variable>AGENTAUTOLOGOFF</variable> on agent login.</para></note>
287 <note><para>The option is ignored unless the ackcall option is enabled.</para></note>
288 <xi:include xpointer="xpointer(/docs/configInfo[@name='app_agent_pool']/description/note)" />
291 <configOption name="wrapuptime">
292 <synopsis>Minimum time the agent has between calls.</synopsis>
294 <para>Set the minimum amount of time in milliseconds after
295 disconnecting a call before the agent can receive a new call.</para>
296 <note><para>The option is overridden by <variable>AGENTWRAPUPTIME</variable> on agent login.</para></note>
297 <xi:include xpointer="xpointer(/docs/configInfo[@name='app_agent_pool']/description/note)" />
300 <configOption name="musiconhold">
301 <synopsis>Music on hold class the agent listens to between calls.</synopsis>
303 <xi:include xpointer="xpointer(/docs/configInfo[@name='app_agent_pool']/description/note)" />
306 <configOption name="recordagentcalls">
307 <synopsis>Enable to automatically record calls the agent takes.</synopsis>
309 <para>Enable recording calls the agent takes automatically by
310 invoking the automixmon DTMF feature when the agent connects
311 to a caller. See <filename>features.conf.sample</filename> for information about
312 the automixmon feature.</para>
313 <xi:include xpointer="xpointer(/docs/configInfo[@name='app_agent_pool']/description/note)" />
316 <configOption name="custom_beep">
317 <synopsis>Sound file played to alert the agent when a call is present.</synopsis>
319 <xi:include xpointer="xpointer(/docs/configInfo[@name='app_agent_pool']/description/note)" />
322 <configOption name="fullname">
323 <synopsis>A friendly name for the agent used in log messages.</synopsis>
325 <xi:include xpointer="xpointer(/docs/configInfo[@name='app_agent_pool']/description/note)" />
333 /* ------------------------------------------------------------------- */
335 #define AST_MAX_BUF 256
337 /*! Maximum wait time (in ms) for the custom_beep file to play announcing the caller. */
338 #define CALLER_SAFETY_TIMEOUT_TIME (2 * 60 * 1000)
340 /*! Number of seconds to wait for local channel optimizations to complete. */
341 #define LOGIN_WAIT_TIMEOUT_TIME 5
343 static const char app_agent_login[] = "AgentLogin";
344 static const char app_agent_request[] = "AgentRequest";
346 /*! Agent config parameters. */
348 AST_DECLARE_STRING_FIELDS(
349 /*! Identification of the agent. (agents config container key) */
350 AST_STRING_FIELD(username);
351 /*! Name of agent for logging and querying purposes */
352 AST_STRING_FIELD(full_name);
355 * \brief DTMF string for an agent to accept a call.
357 * \note The channel variable AGENTACCEPTDTMF overrides on login.
359 AST_STRING_FIELD(dtmf_accept);
360 /*! Beep sound file to use. Alert the agent a call is waiting. */
361 AST_STRING_FIELD(beep_sound);
362 /*! MOH class to use while agent waiting for call. */
363 AST_STRING_FIELD(moh);
366 * \brief Number of seconds for agent to ack a call before being logged off.
368 * \note The channel variable AGENTAUTOLOGOFF overrides on login.
369 * \note If zero then timer is disabled.
371 unsigned int auto_logoff;
373 * \brief Time after a call in ms before the agent can get a new call.
375 * \note The channel variable AGENTWRAPUPTIME overrides on login.
377 unsigned int wrapup_time;
379 * \brief TRUE if agent needs to ack a call to accept it.
381 * \note The channel variable AGENTACKCALL overrides on login.
384 /*! TRUE if agent calls are automatically recorded. */
385 int record_agent_calls;
390 * \brief Agent config ao2 container sort function.
393 * \param obj_left pointer to the (user-defined part) of an object.
394 * \param obj_right pointer to the (user-defined part) of an object.
395 * \param flags flags from ao2_callback()
396 * OBJ_POINTER - if set, 'obj_right', is an object.
397 * OBJ_KEY - if set, 'obj_right', is a search key item that is not an object.
398 * OBJ_PARTIAL_KEY - if set, 'obj_right', is a partial search key item that is not an object.
400 * \retval <0 if obj_left < obj_right
401 * \retval =0 if obj_left == obj_right
402 * \retval >0 if obj_left > obj_right
404 static int agent_cfg_sort_cmp(const void *obj_left, const void *obj_right, int flags)
406 const struct agent_cfg *cfg_left = obj_left;
407 const struct agent_cfg *cfg_right = obj_right;
408 const char *right_key = obj_right;
411 switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
414 right_key = cfg_right->username;
417 cmp = strcmp(cfg_left->username, right_key);
419 case OBJ_PARTIAL_KEY:
420 cmp = strncmp(cfg_left->username, right_key, strlen(right_key));
426 static void agent_cfg_destructor(void *vdoomed)
428 struct agent_cfg *doomed = vdoomed;
430 ast_string_field_free_memory(doomed);
433 static void *agent_cfg_alloc(const char *name)
435 struct agent_cfg *cfg;
437 cfg = ao2_alloc_options(sizeof(*cfg), agent_cfg_destructor,
438 AO2_ALLOC_OPT_LOCK_NOLOCK);
439 if (!cfg || ast_string_field_init(cfg, 64)) {
442 ast_string_field_set(cfg, username, name);
446 static void *agent_cfg_find(struct ao2_container *agents, const char *username)
448 return ao2_find(agents, username, OBJ_KEY);
451 /*! Agents configuration */
453 /*! Master configured agents container. */
454 struct ao2_container *agents;
457 static struct aco_type agent_type = {
460 .category_match = ACO_BLACKLIST,
461 .category = "^(general|agents)$",
462 .item_alloc = agent_cfg_alloc,
463 .item_find = agent_cfg_find,
464 .item_offset = offsetof(struct agents_cfg, agents),
467 static struct aco_type *agent_types[] = ACO_TYPES(&agent_type);
469 /* The general category is reserved, but unused */
470 static struct aco_type general_type = {
473 .category_match = ACO_WHITELIST,
474 .category = "^general$",
477 static struct aco_file agents_conf = {
478 .filename = "agents.conf",
479 .types = ACO_TYPES(&general_type, &agent_type),
482 static AO2_GLOBAL_OBJ_STATIC(cfg_handle);
484 static void agents_cfg_destructor(void *vdoomed)
486 struct agents_cfg *doomed = vdoomed;
488 ao2_cleanup(doomed->agents);
489 doomed->agents = NULL;
494 * \brief Create struct agents_cfg object.
497 * \note A lock is not needed for the object or any secondary
498 * created cfg objects. These objects are immutable after the
499 * config is loaded and applied.
501 * \retval New struct agents_cfg object.
502 * \retval NULL on error.
504 static void *agents_cfg_alloc(void)
506 struct agents_cfg *cfg;
508 cfg = ao2_alloc_options(sizeof(*cfg), agents_cfg_destructor,
509 AO2_ALLOC_OPT_LOCK_NOLOCK);
513 cfg->agents = ao2_container_alloc_rbtree(AO2_ALLOC_OPT_LOCK_NOLOCK,
514 AO2_CONTAINER_ALLOC_OPT_DUPS_REJECT, agent_cfg_sort_cmp, NULL);
522 static void agents_post_apply_config(void);
524 CONFIG_INFO_STANDARD(cfg_info, cfg_handle, agents_cfg_alloc,
525 .files = ACO_FILES(&agents_conf),
526 .post_apply_config = agents_post_apply_config,
529 static void destroy_config(void)
531 ao2_global_obj_release(cfg_handle);
532 aco_info_destroy(&cfg_info);
535 static int load_config(void)
537 if (aco_info_init(&cfg_info)) {
542 aco_option_register(&cfg_info, "ackcall", ACO_EXACT, agent_types, "no", OPT_BOOL_T, 1, FLDSET(struct agent_cfg, ack_call));
543 aco_option_register(&cfg_info, "acceptdtmf", ACO_EXACT, agent_types, "#", OPT_STRINGFIELD_T, 1, STRFLDSET(struct agent_cfg, dtmf_accept));
544 aco_option_register(&cfg_info, "autologoff", ACO_EXACT, agent_types, "0", OPT_UINT_T, 0, FLDSET(struct agent_cfg, auto_logoff));
545 aco_option_register(&cfg_info, "wrapuptime", ACO_EXACT, agent_types, "0", OPT_UINT_T, 0, FLDSET(struct agent_cfg, wrapup_time));
546 aco_option_register(&cfg_info, "musiconhold", ACO_EXACT, agent_types, "default", OPT_STRINGFIELD_T, 0, STRFLDSET(struct agent_cfg, moh));
547 aco_option_register(&cfg_info, "recordagentcalls", ACO_EXACT, agent_types, "no", OPT_BOOL_T, 1, FLDSET(struct agent_cfg, record_agent_calls));
548 aco_option_register(&cfg_info, "custom_beep", ACO_EXACT, agent_types, "beep", OPT_STRINGFIELD_T, 1, STRFLDSET(struct agent_cfg, beep_sound));
549 aco_option_register(&cfg_info, "fullname", ACO_EXACT, agent_types, NULL, OPT_STRINGFIELD_T, 0, STRFLDSET(struct agent_cfg, full_name));
551 if (aco_process_config(&cfg_info, 0) == ACO_PROCESS_ERROR) {
563 /*! The agent is defined but an agent is not present. */
564 AGENT_STATE_LOGGED_OUT,
565 /*! Forced initial login wait to allow any local channel optimizations to happen. */
566 AGENT_STATE_PROBATION_WAIT,
567 /*! The agent is ready for a call. */
568 AGENT_STATE_READY_FOR_CALL,
569 /*! The agent has a call waiting to connect. */
570 AGENT_STATE_CALL_PRESENT,
571 /*! The agent needs to ack the call. */
572 AGENT_STATE_CALL_WAIT_ACK,
573 /*! The agent is connected with a call. */
575 /*! The agent is resting between calls. */
576 AGENT_STATE_CALL_WRAPUP,
577 /*! The agent is being kicked out. */
578 AGENT_STATE_LOGGING_OUT,
581 /*! Agent config option override flags. */
582 enum agent_override_flags {
583 AGENT_FLAG_ACK_CALL = (1 << 0),
584 AGENT_FLAG_DTMF_ACCEPT = (1 << 1),
585 AGENT_FLAG_AUTO_LOGOFF = (1 << 2),
586 AGENT_FLAG_WRAPUP_TIME = (1 << 3),
589 /*! \brief Structure representing an agent. */
591 AST_DECLARE_STRING_FIELDS(
592 /*! Identification of the agent. (agents container key) */
593 AST_STRING_FIELD(username);
594 /*! Login override DTMF string for an agent to accept a call. */
595 AST_STRING_FIELD(override_dtmf_accept);
597 /*! Connected line information to send when reentering the holding bridge. */
598 struct ast_party_connected_line waiting_colp;
599 /*! Flags show if settings were overridden by channel vars. */
601 /*! Login override number of seconds for agent to ack a call before being logged off. */
602 unsigned int override_auto_logoff;
603 /*! Login override time after a call in ms before the agent can get a new call. */
604 unsigned int override_wrapup_time;
605 /*! Login override if agent needs to ack a call to accept it. */
606 unsigned int override_ack_call:1;
608 /*! TRUE if the agent is requested to logoff when the current call ends. */
609 unsigned int deferred_logoff:1;
611 /*! Mark and sweep config update to determine if an agent is dead. */
612 unsigned int the_mark:1;
614 * \brief TRUE if the agent is no longer configured and is being destroyed.
616 * \note Agents cannot log in if they are dead.
620 /*! Agent control state variable. */
621 enum agent_state state;
622 /*! Custom device state of agent. */
623 enum ast_device_state devstate;
625 /*! When agent first logged in */
627 /*! When agent login probation started. */
628 time_t probation_start;
629 /*! When call started */
631 /*! When ack timer started */
632 struct timeval ack_time;
633 /*! When last disconnected */
634 struct timeval last_disconnect;
636 /*! Caller is waiting in this bridge for agent to join. (Holds ref) */
637 struct ast_bridge *caller_bridge;
638 /*! Agent is logged in with this channel. (Holds ref) (NULL if not logged in.) */
639 struct ast_channel *logged;
640 /*! Active config values from config file. (Holds ref) */
641 struct agent_cfg *cfg;
644 /*! Container of defined agents. */
645 static struct ao2_container *agents;
648 * \brief Lock the agent.
650 * \param agent Agent to lock
654 #define agent_lock(agent) _agent_lock(agent, __FILE__, __PRETTY_FUNCTION__, __LINE__, #agent)
655 static inline void _agent_lock(struct agent_pvt *agent, const char *file, const char *function, int line, const char *var)
657 __ao2_lock(agent, AO2_LOCK_REQ_MUTEX, file, function, line, var);
661 * \brief Unlock the agent.
663 * \param agent Agent to unlock
667 #define agent_unlock(agent) _agent_unlock(agent, __FILE__, __PRETTY_FUNCTION__, __LINE__, #agent)
668 static inline void _agent_unlock(struct agent_pvt *agent, const char *file, const char *function, int line, const char *var)
670 __ao2_unlock(agent, file, function, line, var);
675 * \brief Obtain the agent logged in channel lock if it exists.
678 * \param agent Pointer to the LOCKED agent_pvt.
680 * \note Assumes the agent lock is already obtained.
682 * \note Defined locking order is channel lock then agent lock.
686 static struct ast_channel *agent_lock_logged(struct agent_pvt *agent)
688 struct ast_channel *logged;
691 if (!agent->logged) { /* No owner. Nothing to do. */
695 /* If we don't ref the logged, it could be killed when we unlock the agent. */
696 logged = ast_channel_ref(agent->logged);
698 /* Locking logged requires us to lock channel, then agent. */
700 ast_channel_lock(logged);
703 /* Check if logged changed during agent unlock period */
704 if (logged != agent->logged) {
705 /* Channel changed. Unref and do another pass. */
706 ast_channel_unlock(logged);
707 ast_channel_unref(logged);
709 /* Channel stayed the same. Return it. */
717 * \brief Get the Agent:agent_id device state.
720 * \param agent_id Username of the agent.
723 * Search the agents container for the agent and return the
726 * \return Device state of the agent.
728 static enum ast_device_state agent_pvt_devstate_get(const char *agent_id)
730 RAII_VAR(struct agent_pvt *, agent, ao2_find(agents, agent_id, OBJ_KEY), ao2_cleanup);
733 return agent->devstate;
735 return AST_DEVICE_INVALID;
740 * \brief Request an agent device state be updated.
743 * \param agent_id Which agent needs the device state updated.
747 static void agent_devstate_changed(const char *agent_id)
749 ast_devstate_changed(AST_DEVICE_UNKNOWN, AST_DEVSTATE_CACHABLE, "Agent:%s", agent_id);
752 static void agent_pvt_destructor(void *vdoomed)
754 struct agent_pvt *doomed = vdoomed;
756 /* Make sure device state reflects agent destruction. */
757 if (!ast_strlen_zero(doomed->username)) {
758 ast_debug(1, "Agent %s: Destroyed.\n", doomed->username);
759 agent_devstate_changed(doomed->username);
762 ast_party_connected_line_free(&doomed->waiting_colp);
763 if (doomed->caller_bridge) {
764 ast_bridge_destroy(doomed->caller_bridge, AST_CAUSE_USER_BUSY);
765 doomed->caller_bridge = NULL;
767 if (doomed->logged) {
768 doomed->logged = ast_channel_unref(doomed->logged);
770 ao2_cleanup(doomed->cfg);
772 ast_string_field_free_memory(doomed);
775 static struct agent_pvt *agent_pvt_new(struct agent_cfg *cfg)
777 struct agent_pvt *agent;
779 agent = ao2_alloc(sizeof(*agent), agent_pvt_destructor);
783 if (ast_string_field_init(agent, 32)) {
787 ast_string_field_set(agent, username, cfg->username);
788 ast_party_connected_line_init(&agent->waiting_colp);
791 agent->devstate = AST_DEVICE_UNAVAILABLE;
797 * \brief Agents ao2 container sort function.
800 * \param obj_left pointer to the (user-defined part) of an object.
801 * \param obj_right pointer to the (user-defined part) of an object.
802 * \param flags flags from ao2_callback()
803 * OBJ_POINTER - if set, 'obj_right', is an object.
804 * OBJ_KEY - if set, 'obj_right', is a search key item that is not an object.
805 * OBJ_PARTIAL_KEY - if set, 'obj_right', is a partial search key item that is not an object.
807 * \retval <0 if obj_left < obj_right
808 * \retval =0 if obj_left == obj_right
809 * \retval >0 if obj_left > obj_right
811 static int agent_pvt_sort_cmp(const void *obj_left, const void *obj_right, int flags)
813 const struct agent_pvt *agent_left = obj_left;
814 const struct agent_pvt *agent_right = obj_right;
815 const char *right_key = obj_right;
818 switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
821 right_key = agent_right->username;
824 cmp = strcmp(agent_left->username, right_key);
826 case OBJ_PARTIAL_KEY:
827 cmp = strncmp(agent_left->username, right_key, strlen(right_key));
835 * \brief ao2_find() callback function.
839 * found = ao2_find(agents, agent, OBJ_POINTER);
840 * found = ao2_find(agents, "agent-id", OBJ_KEY);
841 * found = ao2_find(agents, agent->logged, 0);
843 static int agent_pvt_cmp(void *obj, void *arg, int flags)
845 const struct agent_pvt *agent = obj;
848 switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
851 case OBJ_PARTIAL_KEY:
855 if (agent->logged == arg) {
865 static int agent_mark(void *obj, void *arg, int flags)
867 struct agent_pvt *agent = obj;
875 static void agents_mark(void)
877 ao2_callback(agents, 0, agent_mark, NULL);
880 static int agent_sweep(void *obj, void *arg, int flags)
882 struct agent_pvt *agent = obj;
886 if (agent->the_mark) {
889 /* Unlink dead agents immediately. */
896 static void agents_sweep(void)
898 struct ao2_iterator *iter;
899 struct agent_pvt *agent;
900 struct ast_channel *logged;
902 iter = ao2_callback(agents, OBJ_MULTIPLE | OBJ_UNLINK, agent_sweep, NULL);
906 for (; (agent = ao2_iterator_next(iter)); ao2_ref(agent, -1)) {
909 logged = ast_channel_ref(agent->logged);
918 "Forced logoff of agent %s(%s). Agent no longer configured.\n",
919 agent->username, ast_channel_name(logged));
920 ast_softhangup(logged, AST_SOFTHANGUP_EXPLICIT);
921 ast_channel_unref(logged);
923 ao2_iterator_destroy(iter);
926 static void agents_post_apply_config(void)
928 struct ao2_iterator iter;
929 struct agent_cfg *cfg;
930 RAII_VAR(struct agents_cfg *, cfgs, ao2_global_obj_ref(cfg_handle), ao2_cleanup);
932 ast_assert(cfgs != NULL);
935 iter = ao2_iterator_init(cfgs->agents, 0);
936 for (; (cfg = ao2_iterator_next(&iter)); ao2_ref(cfg, -1)) {
937 RAII_VAR(struct agent_pvt *, agent, ao2_find(agents, cfg->username, OBJ_KEY), ao2_cleanup);
942 if (!agent->logged) {
943 struct agent_cfg *cfg_old;
945 /* Replace the config of agents not logged in. */
946 cfg_old = agent->cfg;
949 ao2_cleanup(cfg_old);
954 agent = agent_pvt_new(cfg);
958 ao2_link(agents, agent);
959 ast_debug(1, "Agent %s: Created.\n", agent->username);
960 agent_devstate_changed(agent->username);
962 ao2_iterator_destroy(&iter);
966 static int agent_logoff_request(const char *agent_id, int soft)
968 struct ast_channel *logged;
969 RAII_VAR(struct agent_pvt *, agent, ao2_find(agents, agent_id, OBJ_KEY), ao2_cleanup);
976 logged = agent_lock_logged(agent);
979 agent->deferred_logoff = 1;
981 ast_softhangup(logged, AST_SOFTHANGUP_EXPLICIT);
983 ast_channel_unlock(logged);
984 ast_channel_unref(logged);
990 /*! Agent holding bridge instance holder. */
991 static AO2_GLOBAL_OBJ_STATIC(agent_holding);
993 /*! Agent holding bridge deferred creation lock. */
994 AST_MUTEX_DEFINE_STATIC(agent_holding_lock);
998 * \brief Connect the agent with the waiting caller.
1001 * \param bridge_channel Agent channel connecting to the caller.
1002 * \param agent Which agent is connecting to the caller.
1004 * \note The agent is locked on entry and not locked on exit.
1008 static void agent_connect_caller(struct ast_bridge_channel *bridge_channel, struct agent_pvt *agent)
1010 struct ast_bridge *caller_bridge;
1011 int record_agent_calls;
1014 record_agent_calls = agent->cfg->record_agent_calls;
1015 caller_bridge = agent->caller_bridge;
1016 agent->caller_bridge = NULL;
1017 agent->state = AGENT_STATE_ON_CALL;
1018 time(&agent->call_start);
1019 agent_unlock(agent);
1021 if (!caller_bridge) {
1023 ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END,
1024 AST_CAUSE_NORMAL_CLEARING);
1027 res = ast_bridge_move(caller_bridge, bridge_channel->bridge, bridge_channel->chan,
1031 ast_bridge_destroy(caller_bridge, AST_CAUSE_USER_BUSY);
1032 ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END,
1033 AST_CAUSE_NORMAL_CLEARING);
1036 ast_bridge_channel_write_control_data(bridge_channel, AST_CONTROL_ANSWER, NULL, 0);
1038 if (record_agent_calls) {
1039 struct ast_bridge_features_automixmonitor options = {
1040 .start_stop = AUTO_MONITOR_START,
1044 * The agent is in the new bridge so we can invoke the
1045 * mixmonitor hook to only start recording.
1047 ast_bridge_features_do(AST_BRIDGE_BUILTIN_AUTOMIXMON, bridge_channel, &options);
1051 static int bridge_agent_hold_ack(struct ast_bridge_channel *bridge_channel, void *hook_pvt)
1053 struct agent_pvt *agent = hook_pvt;
1056 switch (agent->state) {
1057 case AGENT_STATE_CALL_WAIT_ACK:
1058 /* Connect to caller now. */
1059 ast_debug(1, "Agent %s: Acked call.\n", agent->username);
1060 agent_connect_caller(bridge_channel, agent);/* Will unlock agent. */
1065 agent_unlock(agent);
1069 static int bridge_agent_hold_heartbeat(struct ast_bridge_channel *bridge_channel, void *hook_pvt)
1071 struct agent_pvt *agent = hook_pvt;
1072 int probation_timedout = 0;
1073 int ack_timedout = 0;
1074 int wrapup_timedout = 0;
1075 int deferred_logoff;
1076 unsigned int wrapup_time;
1077 unsigned int auto_logoff;
1080 deferred_logoff = agent->deferred_logoff;
1081 if (deferred_logoff) {
1082 agent->state = AGENT_STATE_LOGGING_OUT;
1085 switch (agent->state) {
1086 case AGENT_STATE_PROBATION_WAIT:
1087 probation_timedout =
1088 LOGIN_WAIT_TIMEOUT_TIME <= (time(NULL) - agent->probation_start);
1089 if (probation_timedout) {
1090 /* Now ready for a caller. */
1091 agent->state = AGENT_STATE_READY_FOR_CALL;
1092 agent->devstate = AST_DEVICE_NOT_INUSE;
1095 case AGENT_STATE_CALL_WAIT_ACK:
1096 /* Check ack call time. */
1097 auto_logoff = agent->cfg->auto_logoff;
1098 if (ast_test_flag(agent, AGENT_FLAG_AUTO_LOGOFF)) {
1099 auto_logoff = agent->override_auto_logoff;
1102 auto_logoff *= 1000;
1103 ack_timedout = ast_tvdiff_ms(ast_tvnow(), agent->ack_time) > auto_logoff;
1105 agent->state = AGENT_STATE_LOGGING_OUT;
1109 case AGENT_STATE_CALL_WRAPUP:
1110 /* Check wrapup time. */
1111 wrapup_time = agent->cfg->wrapup_time;
1112 if (ast_test_flag(agent, AGENT_FLAG_WRAPUP_TIME)) {
1113 wrapup_time = agent->override_wrapup_time;
1115 wrapup_timedout = ast_tvdiff_ms(ast_tvnow(), agent->last_disconnect) > wrapup_time;
1116 if (wrapup_timedout) {
1117 agent->state = AGENT_STATE_READY_FOR_CALL;
1118 agent->devstate = AST_DEVICE_NOT_INUSE;
1124 agent_unlock(agent);
1126 if (deferred_logoff) {
1127 ast_debug(1, "Agent %s: Deferred logoff.\n", agent->username);
1128 ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END,
1129 AST_CAUSE_NORMAL_CLEARING);
1130 } else if (probation_timedout) {
1131 ast_debug(1, "Agent %s: Login complete.\n", agent->username);
1132 agent_devstate_changed(agent->username);
1133 } else if (ack_timedout) {
1134 ast_debug(1, "Agent %s: Ack call timeout.\n", agent->username);
1135 ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END,
1136 AST_CAUSE_NORMAL_CLEARING);
1137 } else if (wrapup_timedout) {
1138 ast_debug(1, "Agent %s: Wrapup timeout. Ready for new call.\n", agent->username);
1139 agent_devstate_changed(agent->username);
1145 static void agent_after_bridge_cb(struct ast_channel *chan, void *data);
1146 static void agent_after_bridge_cb_failed(enum ast_bridge_after_cb_reason reason, void *data);
1150 * \brief ast_bridge agent_hold push method.
1153 * \param self Bridge to operate upon.
1154 * \param bridge_channel Bridge channel to push.
1155 * \param swap Bridge channel to swap places with if not NULL.
1157 * \note On entry, self is already locked.
1159 * \retval 0 on success
1160 * \retval -1 on failure
1162 static int bridge_agent_hold_push(struct ast_bridge *self, struct ast_bridge_channel *bridge_channel, struct ast_bridge_channel *swap)
1165 unsigned int wrapup_time;
1166 char dtmf[AST_FEATURE_MAX_LEN];
1167 struct ast_channel *chan;
1168 const char *moh_class;
1169 RAII_VAR(struct agent_pvt *, agent, NULL, ao2_cleanup);
1171 chan = bridge_channel->chan;
1173 agent = ao2_find(agents, swap ? swap->chan : chan, 0);
1175 /* Could not find the agent. */
1179 /* Setup agent entertainment */
1181 moh_class = ast_strdupa(agent->cfg->moh);
1182 agent_unlock(agent);
1183 res |= ast_channel_add_bridge_role(chan, "holding_participant");
1184 res |= ast_channel_set_bridge_role_option(chan, "holding_participant", "idle_mode", "musiconhold");
1185 res |= ast_channel_set_bridge_role_option(chan, "holding_participant", "moh_class", moh_class);
1187 /* Add DTMF acknowledge hook. */
1190 if (ast_test_flag(agent, AGENT_FLAG_ACK_CALL)
1191 ? agent->override_ack_call : agent->cfg->ack_call) {
1192 const char *dtmf_accept;
1194 dtmf_accept = ast_test_flag(agent, AGENT_FLAG_DTMF_ACCEPT)
1195 ? agent->override_dtmf_accept : agent->cfg->dtmf_accept;
1196 ast_copy_string(dtmf, dtmf_accept, sizeof(dtmf));
1198 agent_unlock(agent);
1199 if (!ast_strlen_zero(dtmf)) {
1201 if (ast_bridge_dtmf_hook(bridge_channel->features, dtmf, bridge_agent_hold_ack,
1202 agent, __ao2_cleanup, AST_BRIDGE_HOOK_REMOVE_ON_PULL)) {
1208 /* Add heartbeat interval hook. */
1210 if (ast_bridge_interval_hook(bridge_channel->features, 0, 1000,
1211 bridge_agent_hold_heartbeat, agent, __ao2_cleanup, AST_BRIDGE_HOOK_REMOVE_ON_PULL)) {
1216 res |= ast_bridge_base_v_table.push(self, bridge_channel, swap);
1218 ast_channel_remove_bridge_role(chan, "holding_participant");
1223 res = ast_bridge_set_after_callback(chan, agent_after_bridge_cb,
1224 agent_after_bridge_cb_failed, chan);
1226 ast_channel_remove_bridge_role(chan, "holding_participant");
1231 ast_channel_unref(agent->logged);
1232 agent->logged = ast_channel_ref(chan);
1233 agent_unlock(agent);
1236 * Kick the channel out so it can come back in fully controlled.
1237 * Otherwise, the after bridge callback will linger and the
1238 * agent will have some slightly different behavior in corner
1241 ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END,
1242 AST_CAUSE_NORMAL_CLEARING);
1247 switch (agent->state) {
1248 case AGENT_STATE_LOGGED_OUT:
1250 * \todo XXX the login probation time should be only if it is needed.
1252 * Need to determine if there are any local channels that can
1253 * optimize and wait until they actually do before leaving the
1254 * AGENT_STATE_PROBATION_WAIT state. For now, the blind
1255 * timer of LOGIN_WAIT_TIMEOUT_TIME will do.
1258 * Start the login probation timer.
1260 * We cannot handle an agent local channel optimization when the
1261 * agent is on a call. The optimization may kick the agent
1262 * channel we know about out of the call without our being able
1263 * to switch to the replacement channel. Get any agent local
1264 * channel optimization out of the way while the agent is in the
1267 time(&agent->probation_start);
1268 agent->state = AGENT_STATE_PROBATION_WAIT;
1269 agent_unlock(agent);
1271 case AGENT_STATE_PROBATION_WAIT:
1272 /* Restart the probation timer. */
1273 time(&agent->probation_start);
1274 agent_unlock(agent);
1276 case AGENT_STATE_READY_FOR_CALL:
1278 * Likely someone manually kicked us out of the holding bridge
1279 * and we came right back in.
1281 agent_unlock(agent);
1284 /* Unexpected agent state. */
1287 case AGENT_STATE_CALL_PRESENT:
1288 case AGENT_STATE_CALL_WAIT_ACK:
1289 agent->state = AGENT_STATE_READY_FOR_CALL;
1290 agent->devstate = AST_DEVICE_NOT_INUSE;
1291 agent_unlock(agent);
1292 ast_debug(1, "Agent %s: Call abort recovery complete.\n", agent->username);
1293 agent_devstate_changed(agent->username);
1295 case AGENT_STATE_ON_CALL:
1296 case AGENT_STATE_CALL_WRAPUP:
1297 wrapup_time = agent->cfg->wrapup_time;
1298 if (ast_test_flag(agent, AGENT_FLAG_WRAPUP_TIME)) {
1299 wrapup_time = agent->override_wrapup_time;
1302 agent->state = AGENT_STATE_CALL_WRAPUP;
1304 agent->state = AGENT_STATE_READY_FOR_CALL;
1305 agent->devstate = AST_DEVICE_NOT_INUSE;
1307 agent_unlock(agent);
1309 /* No wrapup time. */
1310 ast_debug(1, "Agent %s: Ready for new call.\n", agent->username);
1311 agent_devstate_changed(agent->username);
1321 * \brief ast_bridge agent_hold pull method.
1323 * \param self Bridge to operate upon.
1324 * \param bridge_channel Bridge channel to pull.
1327 * Remove any channel hooks controlled by the bridge. Release
1328 * any resources held by bridge_channel->bridge_pvt and release
1329 * bridge_channel->bridge_pvt.
1331 * \note On entry, self is already locked.
1335 static void bridge_agent_hold_pull(struct ast_bridge *self, struct ast_bridge_channel *bridge_channel)
1337 ast_channel_remove_bridge_role(bridge_channel->chan, "holding_participant");
1338 ast_bridge_base_v_table.pull(self, bridge_channel);
1342 * \brief The bridge is being dissolved.
1344 * \param self Bridge to operate upon.
1347 * The bridge is being dissolved. Remove any external
1348 * references to the bridge so it can be destroyed.
1350 * \note On entry, self must NOT be locked.
1354 static void bridge_agent_hold_dissolving(struct ast_bridge *self)
1356 ao2_global_obj_release(agent_holding);
1357 ast_bridge_base_v_table.dissolving(self);
1360 static struct ast_bridge_methods bridge_agent_hold_v_table;
1362 static struct ast_bridge *bridge_agent_hold_new(void)
1364 struct ast_bridge *bridge;
1366 bridge = bridge_alloc(sizeof(struct ast_bridge), &bridge_agent_hold_v_table);
1367 bridge = bridge_base_init(bridge, AST_BRIDGE_CAPABILITY_HOLDING,
1368 AST_BRIDGE_FLAG_MERGE_INHIBIT_TO | AST_BRIDGE_FLAG_MERGE_INHIBIT_FROM
1369 | AST_BRIDGE_FLAG_SWAP_INHIBIT_FROM | AST_BRIDGE_FLAG_TRANSFER_PROHIBITED);
1370 bridge = bridge_register(bridge);
1374 static void bridge_init_agent_hold(void)
1376 /* Setup bridge agent_hold subclass v_table. */
1377 bridge_agent_hold_v_table = ast_bridge_base_v_table;
1378 bridge_agent_hold_v_table.name = "agent_hold";
1379 bridge_agent_hold_v_table.dissolving = bridge_agent_hold_dissolving;
1380 bridge_agent_hold_v_table.push = bridge_agent_hold_push;
1381 bridge_agent_hold_v_table.pull = bridge_agent_hold_pull;
1384 static int bridge_agent_hold_deferred_create(void)
1386 RAII_VAR(struct ast_bridge *, holding, ao2_global_obj_ref(agent_holding), ao2_cleanup);
1389 ast_mutex_lock(&agent_holding_lock);
1390 holding = ao2_global_obj_ref(agent_holding);
1392 holding = bridge_agent_hold_new();
1393 ao2_global_obj_replace_unref(agent_holding, holding);
1395 ast_mutex_unlock(&agent_holding_lock);
1397 ast_log(LOG_ERROR, "Could not create agent holding bridge.\n");
1404 static void send_agent_login(struct ast_channel *chan, const char *agent)
1406 RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
1408 ast_assert(agent != NULL);
1410 blob = ast_json_pack("{s: s}",
1416 ast_channel_publish_blob(chan, ast_channel_agent_login_type(), blob);
1419 static void send_agent_logoff(struct ast_channel *chan, const char *agent, long logintime)
1421 RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
1423 ast_assert(agent != NULL);
1425 blob = ast_json_pack("{s: s, s: i}",
1427 "logintime", logintime);
1432 ast_channel_publish_blob(chan, ast_channel_agent_logoff_type(), blob);
1437 * \brief Logout the agent.
1440 * \param agent Which agent logging out.
1442 * \note On entry agent is already locked. On exit it is no longer locked.
1446 static void agent_logout(struct agent_pvt *agent)
1448 struct ast_channel *logged;
1449 struct ast_bridge *caller_bridge;
1450 long time_logged_in;
1452 time_logged_in = time(NULL) - agent->login_start;
1453 logged = agent->logged;
1454 agent->logged = NULL;
1455 caller_bridge = agent->caller_bridge;
1456 agent->caller_bridge = NULL;
1457 agent->state = AGENT_STATE_LOGGED_OUT;
1458 agent->devstate = AST_DEVICE_UNAVAILABLE;
1459 ast_clear_flag(agent, AST_FLAGS_ALL);
1460 agent_unlock(agent);
1461 agent_devstate_changed(agent->username);
1463 if (caller_bridge) {
1464 ast_bridge_destroy(caller_bridge, AST_CAUSE_USER_BUSY);
1467 send_agent_logoff(logged, agent->username, time_logged_in);
1468 ast_verb(2, "Agent '%s' logged out. Logged in for %ld seconds.\n",
1469 agent->username, time_logged_in);
1470 ast_channel_unref(logged);
1475 * \brief Agent driver loop.
1478 * \param agent Which agent.
1479 * \param logged The logged in channel.
1483 static void agent_run(struct agent_pvt *agent, struct ast_channel *logged)
1485 struct ast_bridge_features features;
1487 if (ast_bridge_features_init(&features)) {
1488 ast_channel_hangupcause_set(logged, AST_CAUSE_NORMAL_CLEARING);
1489 goto agent_run_cleanup;
1492 struct agents_cfg *cfgs;
1493 struct agent_cfg *cfg_new;
1494 struct agent_cfg *cfg_old;
1495 struct ast_bridge *holding;
1496 struct ast_bridge *caller_bridge;
1498 ast_channel_hangupcause_set(logged, AST_CAUSE_NORMAL_CLEARING);
1500 holding = ao2_global_obj_ref(agent_holding);
1502 ast_debug(1, "Agent %s: Someone destroyed the agent holding bridge.\n",
1508 * When the agent channel leaves the bridging system we usually
1509 * want to put the agent back into the holding bridge for the
1512 ast_bridge_join(holding, logged, NULL, &features, NULL,
1513 AST_BRIDGE_JOIN_PASS_REFERENCE);
1514 if (logged != agent->logged) {
1515 /* This channel is no longer the logged in agent. */
1520 /* The agent is no longer configured. */
1524 /* Update the agent's config before rejoining the holding bridge. */
1525 cfgs = ao2_global_obj_ref(cfg_handle);
1527 /* There is no agent configuration. All agents were destroyed. */
1530 cfg_new = ao2_find(cfgs->agents, agent->username, OBJ_KEY);
1533 /* The agent is no longer configured. */
1537 cfg_old = agent->cfg;
1538 agent->cfg = cfg_new;
1540 agent->last_disconnect = ast_tvnow();
1542 /* Clear out any caller bridge before rejoining the holding bridge. */
1543 caller_bridge = agent->caller_bridge;
1544 agent->caller_bridge = NULL;
1545 agent_unlock(agent);
1546 ao2_ref(cfg_old, -1);
1547 if (caller_bridge) {
1548 ast_bridge_destroy(caller_bridge, AST_CAUSE_USER_BUSY);
1551 if (agent->state == AGENT_STATE_LOGGING_OUT
1552 || agent->deferred_logoff
1553 || ast_check_hangup_locked(logged)) {
1554 /* The agent was requested to logout or hungup. */
1559 * It is safe to access agent->waiting_colp without a lock. It
1560 * is only setup on agent login and not changed.
1562 ast_channel_update_connected_line(logged, &agent->waiting_colp, NULL);
1564 ast_bridge_features_cleanup(&features);
1568 if (logged != agent->logged) {
1570 * We are no longer the agent channel because of local channel
1573 agent_unlock(agent);
1574 ast_debug(1, "Agent %s: Channel %s is no longer the agent.\n",
1575 agent->username, ast_channel_name(logged));
1578 agent_logout(agent);
1581 static void agent_after_bridge_cb(struct ast_channel *chan, void *data)
1583 struct agent_pvt *agent;
1585 agent = ao2_find(agents, chan, 0);
1590 ast_debug(1, "Agent %s: New agent channel %s.\n",
1591 agent->username, ast_channel_name(chan));
1592 agent_run(agent, chan);
1596 static void agent_after_bridge_cb_failed(enum ast_bridge_after_cb_reason reason, void *data)
1598 struct ast_channel *chan = data;
1599 struct agent_pvt *agent;
1601 agent = ao2_find(agents, chan, 0);
1605 ast_log(LOG_WARNING, "Agent %s: Forced logout. Lost control of %s because: %s\n",
1606 agent->username, ast_channel_name(chan),
1607 ast_bridge_after_cb_reason_string(reason));
1609 agent_logout(agent);
1615 * \brief Get the lock on the agent bridge_channel and return it.
1618 * \param agent Whose bridge_channel to get.
1620 * \retval bridge_channel on success (Reffed and locked).
1621 * \retval NULL on error.
1623 static struct ast_bridge_channel *agent_bridge_channel_get_lock(struct agent_pvt *agent)
1625 struct ast_channel *logged;
1626 struct ast_bridge_channel *bc;
1630 logged = agent->logged;
1632 agent_unlock(agent);
1635 ast_channel_ref(logged);
1636 agent_unlock(agent);
1638 ast_channel_lock(logged);
1639 bc = ast_channel_get_bridge_channel(logged);
1640 ast_channel_unlock(logged);
1641 ast_channel_unref(logged);
1643 if (agent->logged != logged) {
1649 ast_bridge_channel_lock(bc);
1650 if (bc->chan != logged || agent->logged != logged) {
1651 ast_bridge_channel_unlock(bc);
1659 static void caller_abort_agent(struct agent_pvt *agent)
1661 struct ast_bridge_channel *logged;
1663 logged = agent_bridge_channel_get_lock(agent);
1665 struct ast_bridge *caller_bridge;
1667 ast_debug(1, "Agent '%s' no longer logged in.\n", agent->username);
1670 caller_bridge = agent->caller_bridge;
1671 agent->caller_bridge = NULL;
1672 agent_unlock(agent);
1673 if (caller_bridge) {
1674 ast_bridge_destroy(caller_bridge, AST_CAUSE_USER_BUSY);
1679 /* Kick the agent out of the holding bridge to reset it. */
1680 ast_bridge_channel_leave_bridge_nolock(logged, BRIDGE_CHANNEL_STATE_END,
1681 AST_CAUSE_NORMAL_CLEARING);
1682 ast_bridge_channel_unlock(logged);
1685 static int caller_safety_timeout(struct ast_bridge_channel *bridge_channel, void *hook_pvt)
1687 struct agent_pvt *agent = hook_pvt;
1689 if (agent->state == AGENT_STATE_CALL_PRESENT) {
1690 ast_verb(3, "Agent '%s' did not respond. Safety timeout.\n", agent->username);
1691 ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END,
1692 AST_CAUSE_USER_BUSY);
1693 caller_abort_agent(agent);
1699 static void agent_alert(struct ast_bridge_channel *bridge_channel, const void *payload, size_t payload_size)
1701 const char *agent_id = payload;
1702 const char *playfile;
1703 RAII_VAR(struct agent_pvt *, agent, NULL, ao2_cleanup);
1705 agent = ao2_find(agents, agent_id, OBJ_KEY);
1707 ast_debug(1, "Agent '%s' does not exist. Where did it go?\n", agent_id);
1711 /* Change holding bridge participant role's idle mode to silence */
1712 ast_bridge_channel_lock_bridge(bridge_channel);
1713 ast_bridge_channel_clear_roles(bridge_channel);
1714 ast_channel_set_bridge_role_option(bridge_channel->chan, "holding_participant", "idle_mode", "silence");
1715 ast_bridge_channel_establish_roles(bridge_channel);
1716 ast_bridge_unlock(bridge_channel->bridge);
1718 /* Alert the agent. */
1720 playfile = ast_strdupa(agent->cfg->beep_sound);
1721 agent_unlock(agent);
1722 ast_stream_and_wait(bridge_channel->chan, playfile, AST_DIGIT_NONE);
1725 switch (agent->state) {
1726 case AGENT_STATE_CALL_PRESENT:
1727 if (ast_test_flag(agent, AGENT_FLAG_ACK_CALL)
1728 ? agent->override_ack_call : agent->cfg->ack_call) {
1729 agent->state = AGENT_STATE_CALL_WAIT_ACK;
1730 agent->ack_time = ast_tvnow();
1734 /* Connect to caller now. */
1735 ast_debug(1, "Agent %s: Immediately connecting to call.\n", agent->username);
1736 agent_connect_caller(bridge_channel, agent);/* Will unlock agent. */
1741 agent_unlock(agent);
1744 static int send_alert_to_agent(struct ast_bridge_channel *bridge_channel, const char *agent_id)
1746 return ast_bridge_channel_queue_callback(bridge_channel,
1747 AST_BRIDGE_CHANNEL_CB_OPTION_MEDIA, agent_alert, agent_id, strlen(agent_id) + 1);
1750 static int send_colp_to_agent(struct ast_bridge_channel *bridge_channel, struct ast_party_connected_line *connected)
1752 struct ast_set_party_connected_line update = {
1757 unsigned char data[1024]; /* This should be large enough */
1760 datalen = ast_connected_line_build_data(data, sizeof(data), connected, &update);
1761 if (datalen == (size_t) -1) {
1765 return ast_bridge_channel_queue_control_data(bridge_channel,
1766 AST_CONTROL_CONNECTED_LINE, data, datalen);
1770 * \brief Dialplan AgentRequest application to locate an agent to talk with.
1772 * \param chan Channel wanting to talk with an agent.
1773 * \param data Application parameters
1775 * \retval 0 To continue in dialplan.
1776 * \retval -1 To hangup.
1778 static int agent_request_exec(struct ast_channel *chan, const char *data)
1780 struct ast_bridge *caller_bridge;
1781 struct ast_bridge_channel *logged;
1784 struct ast_bridge_features caller_features;
1785 struct ast_party_connected_line connected;
1786 AST_DECLARE_APP_ARGS(args,
1787 AST_APP_ARG(agent_id);
1788 AST_APP_ARG(other); /* Any remaining unused arguments */
1791 RAII_VAR(struct agent_pvt *, agent, NULL, ao2_cleanup);
1793 if (bridge_agent_hold_deferred_create()) {
1797 parse = ast_strdupa(data);
1798 AST_STANDARD_APP_ARGS(args, parse);
1800 if (ast_strlen_zero(args.agent_id)) {
1801 ast_log(LOG_WARNING, "AgentRequest requires an AgentId\n");
1805 /* Find the agent. */
1806 agent = ao2_find(agents, args.agent_id, OBJ_KEY);
1808 ast_verb(3, "Agent '%s' does not exist.\n", args.agent_id);
1809 pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "INVALID");
1813 if (ast_bridge_features_init(&caller_features)) {
1817 /* Add safety timeout hook. */
1819 if (ast_bridge_interval_hook(&caller_features, 0, CALLER_SAFETY_TIMEOUT_TIME,
1820 caller_safety_timeout, agent, __ao2_cleanup, AST_BRIDGE_HOOK_REMOVE_ON_PULL)) {
1822 ast_bridge_features_cleanup(&caller_features);
1826 caller_bridge = ast_bridge_basic_new();
1827 if (!caller_bridge) {
1828 ast_bridge_features_cleanup(&caller_features);
1832 /* Get COLP for agent. */
1833 ast_party_connected_line_init(&connected);
1834 ast_channel_lock(chan);
1835 ast_connected_line_copy_from_caller(&connected, ast_channel_caller(chan));
1836 ast_channel_unlock(chan);
1839 switch (agent->state) {
1840 case AGENT_STATE_LOGGED_OUT:
1841 case AGENT_STATE_LOGGING_OUT:
1842 agent_unlock(agent);
1843 ast_party_connected_line_free(&connected);
1844 ast_bridge_destroy(caller_bridge, 0);
1845 ast_bridge_features_cleanup(&caller_features);
1846 ast_verb(3, "Agent '%s' not logged in.\n", agent->username);
1847 pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "NOT_LOGGED_IN");
1849 case AGENT_STATE_READY_FOR_CALL:
1850 ao2_ref(caller_bridge, +1);
1851 agent->caller_bridge = caller_bridge;
1852 agent->state = AGENT_STATE_CALL_PRESENT;
1853 agent->devstate = AST_DEVICE_INUSE;
1856 agent_unlock(agent);
1857 ast_party_connected_line_free(&connected);
1858 ast_bridge_destroy(caller_bridge, 0);
1859 ast_bridge_features_cleanup(&caller_features);
1860 ast_verb(3, "Agent '%s' is busy.\n", agent->username);
1861 pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "BUSY");
1864 agent_unlock(agent);
1865 agent_devstate_changed(agent->username);
1867 logged = agent_bridge_channel_get_lock(agent);
1869 ast_party_connected_line_free(&connected);
1870 ast_bridge_destroy(caller_bridge, 0);
1871 ast_bridge_features_cleanup(&caller_features);
1872 ast_verb(3, "Agent '%s' not logged in.\n", agent->username);
1873 pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "NOT_LOGGED_IN");
1874 caller_abort_agent(agent);
1878 send_colp_to_agent(logged, &connected);
1879 ast_party_connected_line_free(&connected);
1881 res = send_alert_to_agent(logged, agent->username);
1882 ast_bridge_channel_unlock(logged);
1883 ao2_ref(logged, -1);
1885 ast_bridge_destroy(caller_bridge, 0);
1886 ast_bridge_features_cleanup(&caller_features);
1887 ast_verb(3, "Agent '%s': Failed to alert the agent.\n", agent->username);
1888 pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "ERROR");
1889 caller_abort_agent(agent);
1893 ast_indicate(chan, AST_CONTROL_RINGING);
1894 ast_bridge_join(caller_bridge, chan, NULL, &caller_features, NULL,
1895 AST_BRIDGE_JOIN_PASS_REFERENCE);
1896 ast_bridge_features_cleanup(&caller_features);
1903 * \brief Get agent config values from the login channel.
1906 * \param agent What to setup channel config values on.
1907 * \param chan Channel logging in as an agent.
1911 static void agent_login_channel_config(struct agent_pvt *agent, struct ast_channel *chan)
1913 struct ast_flags opts = { 0 };
1914 struct ast_party_connected_line connected;
1915 unsigned int override_ack_call = 0;
1916 unsigned int override_auto_logoff = 0;
1917 unsigned int override_wrapup_time = 0;
1918 const char *override_dtmf_accept = NULL;
1921 ast_party_connected_line_init(&connected);
1923 /* Get config values from channel. */
1924 ast_channel_lock(chan);
1925 ast_party_connected_line_copy(&connected, ast_channel_connected(chan));
1927 var = pbx_builtin_getvar_helper(chan, "AGENTACKCALL");
1928 if (!ast_strlen_zero(var)) {
1929 override_ack_call = ast_true(var) ? 1 : 0;
1930 ast_set_flag(&opts, AGENT_FLAG_ACK_CALL);
1933 var = pbx_builtin_getvar_helper(chan, "AGENTACCEPTDTMF");
1934 if (!ast_strlen_zero(var)) {
1935 override_dtmf_accept = ast_strdupa(var);
1936 ast_set_flag(&opts, AGENT_FLAG_DTMF_ACCEPT);
1939 var = pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF");
1940 if (!ast_strlen_zero(var)) {
1941 if (sscanf(var, "%u", &override_auto_logoff) == 1) {
1942 ast_set_flag(&opts, AGENT_FLAG_AUTO_LOGOFF);
1946 var = pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME");
1947 if (!ast_strlen_zero(var)) {
1948 if (sscanf(var, "%u", &override_wrapup_time) == 1) {
1949 ast_set_flag(&opts, AGENT_FLAG_WRAPUP_TIME);
1952 ast_channel_unlock(chan);
1954 /* Set config values on agent. */
1956 ast_party_connected_line_free(&agent->waiting_colp);
1957 agent->waiting_colp = connected;
1959 ast_string_field_set(agent, override_dtmf_accept, override_dtmf_accept);
1960 ast_copy_flags(agent, &opts, AST_FLAGS_ALL);
1961 agent->override_auto_logoff = override_auto_logoff;
1962 agent->override_wrapup_time = override_wrapup_time;
1963 agent->override_ack_call = override_ack_call;
1964 agent_unlock(agent);
1967 enum AGENT_LOGIN_OPT_FLAGS {
1968 OPT_SILENT = (1 << 0),
1970 AST_APP_OPTIONS(agent_login_opts, BEGIN_OPTIONS
1971 AST_APP_OPTION('s', OPT_SILENT),
1975 * \brief Dialplan AgentLogin application to log in an agent.
1977 * \param chan Channel attempting to login as an agent.
1978 * \param data Application parameters
1980 * \retval 0 To continue in dialplan.
1981 * \retval -1 To hangup.
1983 static int agent_login_exec(struct ast_channel *chan, const char *data)
1986 struct ast_flags opts;
1987 AST_DECLARE_APP_ARGS(args,
1988 AST_APP_ARG(agent_id);
1989 AST_APP_ARG(options);
1990 AST_APP_ARG(other); /* Any remaining unused arguments */
1993 RAII_VAR(struct agent_pvt *, agent, NULL, ao2_cleanup);
1995 if (bridge_agent_hold_deferred_create()) {
1999 if (ast_channel_state(chan) != AST_STATE_UP && ast_answer(chan)) {
2003 parse = ast_strdupa(data);
2004 AST_STANDARD_APP_ARGS(args, parse);
2006 if (ast_strlen_zero(args.agent_id)) {
2007 ast_log(LOG_WARNING, "AgentLogin requires an AgentId\n");
2011 if (ast_app_parse_options(agent_login_opts, &opts, NULL, args.options)) {
2012 /* General invalid option syntax. */
2016 /* Find the agent. */
2017 agent = ao2_find(agents, args.agent_id, OBJ_KEY);
2019 ast_verb(3, "Agent '%s' does not exist.\n", args.agent_id);
2020 pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "INVALID");
2024 /* Has someone already logged in as this agent already? */
2026 if (agent->logged) {
2027 agent_unlock(agent);
2028 ast_verb(3, "Agent '%s' already logged in.\n", agent->username);
2029 pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "ALREADY_LOGGED_IN");
2032 agent->logged = ast_channel_ref(chan);
2033 agent->last_disconnect = ast_tvnow();
2034 time(&agent->login_start);
2035 agent->deferred_logoff = 0;
2036 agent_unlock(agent);
2038 agent_login_channel_config(agent, chan);
2040 if (!ast_test_flag(&opts, OPT_SILENT)
2041 && !ast_streamfile(chan, "agent-loginok", ast_channel_language(chan))) {
2042 ast_waitstream(chan, "");
2045 ast_verb(2, "Agent '%s' logged in (format %s/%s)\n", agent->username,
2046 ast_getformatname(ast_channel_readformat(chan)),
2047 ast_getformatname(ast_channel_writeformat(chan)));
2048 send_agent_login(chan, agent->username);
2050 agent_run(agent, chan);
2054 static int agent_function_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
2057 struct agent_pvt *agent;
2058 struct ast_channel *logged;
2059 AST_DECLARE_APP_ARGS(args,
2060 AST_APP_ARG(agentid);
2066 parse = ast_strdupa(data ?: "");
2067 AST_NONSTANDARD_APP_ARGS(args, parse, ':');
2069 if (ast_strlen_zero(args.agentid)) {
2070 ast_log(LOG_WARNING, "The AGENT function requires an argument - agentid!\n");
2074 args.item = "status";
2077 agent = ao2_find(agents, args.agentid, OBJ_KEY);
2079 ast_log(LOG_WARNING, "Agent '%s' not found!\n", args.agentid);
2084 if (!strcasecmp(args.item, "status")) {
2087 if (agent->logged) {
2088 status = "LOGGEDIN";
2090 status = "LOGGEDOUT";
2092 ast_copy_string(buf, status, len);
2093 } else if (!strcasecmp(args.item, "name")) {
2094 ast_copy_string(buf, agent->cfg->full_name, len);
2095 } else if (!strcasecmp(args.item, "mohclass")) {
2096 ast_copy_string(buf, agent->cfg->moh, len);
2097 } else if (!strcasecmp(args.item, "channel")) {
2098 logged = agent_lock_logged(agent);
2102 ast_copy_string(buf, ast_channel_name(logged), len);
2103 ast_channel_unlock(logged);
2104 ast_channel_unref(logged);
2106 pos = strrchr(buf, '-');
2111 } else if (!strcasecmp(args.item, "fullchannel")) {
2112 logged = agent_lock_logged(agent);
2114 ast_copy_string(buf, ast_channel_name(logged), len);
2115 ast_channel_unlock(logged);
2116 ast_channel_unref(logged);
2119 agent_unlock(agent);
2125 static struct ast_custom_function agent_function = {
2127 .read = agent_function_read,
2130 struct agent_complete {
2131 /*! Nth match to return. */
2133 /*! Which match currently on. */
2137 static int complete_agent_search(void *obj, void *arg, void *data, int flags)
2139 struct agent_complete *search = data;
2141 if (++search->which > search->state) {
2147 static char *complete_agent(const char *word, int state)
2150 struct agent_pvt *agent;
2151 struct agent_complete search = {
2155 agent = ao2_callback_data(agents, ast_strlen_zero(word) ? 0 : OBJ_PARTIAL_KEY,
2156 complete_agent_search, (char *) word, &search);
2160 ret = ast_strdup(agent->username);
2165 static int complete_agent_logoff_search(void *obj, void *arg, void *data, int flags)
2167 struct agent_pvt *agent = obj;
2168 struct agent_complete *search = data;
2170 if (!agent->logged) {
2173 if (++search->which > search->state) {
2179 static char *complete_agent_logoff(const char *word, int state)
2182 struct agent_pvt *agent;
2183 struct agent_complete search = {
2187 agent = ao2_callback_data(agents, ast_strlen_zero(word) ? 0 : OBJ_PARTIAL_KEY,
2188 complete_agent_logoff_search, (char *) word, &search);
2192 ret = ast_strdup(agent->username);
2197 static void agent_show_requested(struct ast_cli_args *a, int online_only)
2199 #define FORMAT_HDR "%-8s %-20s %-11s %-30s %s\n"
2200 #define FORMAT_ROW "%-8s %-20s %-11s %-30s %s\n"
2202 struct ao2_iterator iter;
2203 struct agent_pvt *agent;
2204 struct ast_str *out = ast_str_alloca(512);
2205 unsigned int agents_total = 0;
2206 unsigned int agents_logged_in = 0;
2207 unsigned int agents_talking = 0;
2209 ast_cli(a->fd, FORMAT_HDR, "Agent-ID", "Name", "State", "Channel", "Talking with");
2210 iter = ao2_iterator_init(agents, 0);
2211 for (; (agent = ao2_iterator_next(&iter)); ao2_ref(agent, -1)) {
2212 struct ast_channel *logged;
2217 logged = agent_lock_logged(agent);
2219 const char *talking_with;
2223 talking_with = pbx_builtin_getvar_helper(logged, "BRIDGEPEER");
2224 if (!ast_strlen_zero(talking_with)) {
2229 ast_str_set(&out, 0, FORMAT_ROW, agent->username, agent->cfg->full_name,
2230 ast_devstate_str(agent->devstate), ast_channel_name(logged), talking_with);
2231 ast_channel_unlock(logged);
2232 ast_channel_unref(logged);
2234 ast_str_set(&out, 0, FORMAT_ROW, agent->username, agent->cfg->full_name,
2235 ast_devstate_str(agent->devstate), "", "");
2237 agent_unlock(agent);
2239 if (!online_only || logged) {
2240 ast_cli(a->fd, "%s", ast_str_buffer(out));
2243 ao2_iterator_destroy(&iter);
2245 ast_cli(a->fd, "\nDefined agents: %u, Logged in: %u, Talking: %u\n",
2246 agents_total, agents_logged_in, agents_talking);
2252 static char *agent_handle_show_online(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2256 e->command = "agent show online";
2258 "Usage: agent show online\n"
2259 " Provides summary information for logged in agents.\n";
2266 return CLI_SHOWUSAGE;
2269 agent_show_requested(a, 1);
2274 static char *agent_handle_show_all(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2278 e->command = "agent show all";
2280 "Usage: agent show all\n"
2281 " Provides summary information for all agents.\n";
2288 return CLI_SHOWUSAGE;
2291 agent_show_requested(a, 0);
2296 static char *agent_handle_show_specific(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2298 struct agent_pvt *agent;
2299 struct ast_channel *logged;
2300 struct ast_str *out = ast_str_alloca(4096);
2304 e->command = "agent show";
2306 "Usage: agent show <agent-id>\n"
2307 " Show information about the <agent-id> agent\n";
2311 return complete_agent(a->word, a->n);
2317 return CLI_SHOWUSAGE;
2320 agent = ao2_find(agents, a->argv[2], OBJ_KEY);
2322 ast_cli(a->fd, "Agent '%s' not found\n", a->argv[2]);
2327 logged = agent_lock_logged(agent);
2328 ast_str_set(&out, 0, "Id: %s\n", agent->username);
2329 ast_str_append(&out, 0, "Name: %s\n", agent->cfg->full_name);
2330 ast_str_append(&out, 0, "Beep: %s\n", agent->cfg->beep_sound);
2331 ast_str_append(&out, 0, "MOH: %s\n", agent->cfg->moh);
2332 ast_str_append(&out, 0, "RecordCalls: %s\n", AST_CLI_YESNO(agent->cfg->record_agent_calls));
2333 ast_str_append(&out, 0, "State: %s\n", ast_devstate_str(agent->devstate));
2335 const char *talking_with;
2337 ast_str_append(&out, 0, "LoggedInChannel: %s\n", ast_channel_name(logged));
2338 ast_str_append(&out, 0, "LoggedInTime: %ld\n", (long) agent->login_start);
2339 talking_with = pbx_builtin_getvar_helper(logged, "BRIDGEPEER");
2340 if (!ast_strlen_zero(talking_with)) {
2341 ast_str_append(&out, 0, "TalkingWith: %s\n", talking_with);
2342 ast_str_append(&out, 0, "CallStarted: %ld\n", (long) agent->call_start);
2344 ast_channel_unlock(logged);
2345 ast_channel_unref(logged);
2347 agent_unlock(agent);
2350 ast_cli(a->fd, "%s", ast_str_buffer(out));
2355 static char *agent_handle_logoff_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2359 e->command = "agent logoff";
2361 "Usage: agent logoff <agent-id> [soft]\n"
2362 " Sets an agent as no longer logged in.\n"
2363 " If 'soft' is specified, do not hangup existing calls.\n";
2367 return complete_agent_logoff(a->word, a->n);
2368 } else if (a->pos == 3 && a->n == 0
2369 && (ast_strlen_zero(a->word)
2370 || !strncasecmp("soft", a->word, strlen(a->word)))) {
2371 return ast_strdup("soft");
2376 if (a->argc < 3 || 4 < a->argc) {
2377 return CLI_SHOWUSAGE;
2379 if (a->argc == 4 && strcasecmp(a->argv[3], "soft")) {
2380 return CLI_SHOWUSAGE;
2383 if (!agent_logoff_request(a->argv[2], a->argc == 4)) {
2384 ast_cli(a->fd, "Logging out %s\n", a->argv[2]);
2390 static struct ast_cli_entry cli_agents[] = {
2391 AST_CLI_DEFINE(agent_handle_show_online, "Show status of online agents"),
2392 AST_CLI_DEFINE(agent_handle_show_all, "Show status of all agents"),
2393 AST_CLI_DEFINE(agent_handle_show_specific, "Show information about an agent"),
2394 AST_CLI_DEFINE(agent_handle_logoff_cmd, "Sets an agent offline"),
2397 static int action_agents(struct mansession *s, const struct message *m)
2399 const char *id = astman_get_header(m, "ActionID");
2400 char id_text[AST_MAX_BUF];
2401 struct ao2_iterator iter;
2402 struct agent_pvt *agent;
2403 struct ast_str *out = ast_str_alloca(4096);
2405 if (!ast_strlen_zero(id)) {
2406 snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", id);
2410 astman_send_ack(s, m, "Agents will follow");
2412 iter = ao2_iterator_init(agents, 0);
2413 for (; (agent = ao2_iterator_next(&iter)); ao2_ref(agent, -1)) {
2414 struct ast_channel *logged;
2417 logged = agent_lock_logged(agent);
2421 * AGENT_LOGGEDOFF - Agent isn't logged in
2422 * AGENT_IDLE - Agent is logged in, and waiting for call
2423 * AGENT_ONCALL - Agent is logged in, and on a call
2424 * AGENT_UNKNOWN - Don't know anything about agent. Shouldn't ever get this.
2426 ast_str_set(&out, 0, "Agent: %s\r\n", agent->username);
2427 ast_str_append(&out, 0, "Name: %s\r\n", agent->cfg->full_name);
2430 const char *talking_to_chan;
2431 struct ast_str *logged_headers;
2432 RAII_VAR(struct ast_channel_snapshot *, logged_snapshot, ast_channel_snapshot_create(logged), ao2_cleanup);
2434 if (!logged_snapshot
2435 || !(logged_headers =
2436 ast_manager_build_channel_state_string(logged_snapshot))) {
2437 ast_channel_unlock(logged);
2438 ast_channel_unref(logged);
2439 agent_unlock(agent);
2443 talking_to_chan = pbx_builtin_getvar_helper(logged, "BRIDGEPEER");
2444 if (!ast_strlen_zero(talking_to_chan)) {
2445 ast_str_append(&out, 0, "Status: %s\r\n", "AGENT_ONCALL");
2446 ast_str_append(&out, 0, "TalkingToChan: %s\r\n", talking_to_chan);
2447 ast_str_append(&out, 0, "CallStarted: %ld\n", (long) agent->call_start);
2449 ast_str_append(&out, 0, "Status: %s\r\n", "AGENT_IDLE");
2451 ast_str_append(&out, 0, "LoggedInTime: %ld\r\n", (long) agent->login_start);
2452 ast_str_append(&out, 0, "%s", ast_str_buffer(logged_headers));
2453 ast_channel_unlock(logged);
2454 ast_channel_unref(logged);
2455 ast_free(logged_headers);
2457 ast_str_append(&out, 0, "Status: %s\r\n", "AGENT_LOGGEDOFF");
2460 agent_unlock(agent);
2462 astman_append(s, "Event: Agents\r\n"
2464 ast_str_buffer(out), id_text);
2466 ao2_iterator_destroy(&iter);
2468 astman_append(s, "Event: AgentsComplete\r\n"
2474 static int action_agent_logoff(struct mansession *s, const struct message *m)
2476 const char *agent = astman_get_header(m, "Agent");
2477 const char *soft_s = astman_get_header(m, "Soft"); /* "true" is don't hangup */
2479 if (ast_strlen_zero(agent)) {
2480 astman_send_error(s, m, "No agent specified");
2484 if (!agent_logoff_request(agent, ast_true(soft_s))) {
2485 astman_send_ack(s, m, "Agent logged out");
2487 astman_send_error(s, m, "No such agent");
2493 static int unload_module(void)
2495 struct ast_bridge *holding;
2497 /* Unregister dialplan applications */
2498 ast_unregister_application(app_agent_login);
2499 ast_unregister_application(app_agent_request);
2501 /* Unregister dialplan functions */
2502 ast_custom_function_unregister(&agent_function);
2504 /* Unregister manager command */
2505 ast_manager_unregister("Agents");
2506 ast_manager_unregister("AgentLogoff");
2508 /* Unregister CLI commands */
2509 ast_cli_unregister_multiple(cli_agents, ARRAY_LEN(cli_agents));
2511 ast_devstate_prov_del("Agent");
2513 /* Destroy agent holding bridge. */
2514 holding = ao2_global_obj_replace(agent_holding, NULL);
2516 ast_bridge_destroy(holding, 0);
2520 ao2_ref(agents, -1);
2525 static int load_module(void)
2529 agents = ao2_container_alloc_rbtree(AO2_ALLOC_OPT_LOCK_MUTEX,
2530 AO2_CONTAINER_ALLOC_OPT_DUPS_REPLACE, agent_pvt_sort_cmp, agent_pvt_cmp);
2532 return AST_MODULE_LOAD_FAILURE;
2534 if (load_config()) {
2535 ast_log(LOG_ERROR, "Unable to load config. Not loading module.\n");
2536 ao2_ref(agents, -1);
2538 return AST_MODULE_LOAD_DECLINE;
2541 /* Init agent holding bridge v_table. */
2542 bridge_init_agent_hold();
2544 /* Setup to provide Agent:agent-id device state. */
2545 res |= ast_devstate_prov_add("Agent", agent_pvt_devstate_get);
2548 res |= ast_cli_register_multiple(cli_agents, ARRAY_LEN(cli_agents));
2550 /* Manager commands */
2551 res |= ast_manager_register_xml("Agents", EVENT_FLAG_AGENT, action_agents);
2552 res |= ast_manager_register_xml("AgentLogoff", EVENT_FLAG_AGENT, action_agent_logoff);
2554 /* Dialplan Functions */
2555 res |= ast_custom_function_register(&agent_function);
2557 /* Dialplan applications */
2558 res |= ast_register_application_xml(app_agent_login, agent_login_exec);
2559 res |= ast_register_application_xml(app_agent_request, agent_request_exec);
2563 return AST_MODULE_LOAD_FAILURE;
2565 return AST_MODULE_LOAD_SUCCESS;
2568 static int reload(void)
2570 if (aco_process_config(&cfg_info, 1) == ACO_PROCESS_ERROR) {
2571 /* Just keep the config we already have in place. */
2577 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Call center agent pool applications",
2578 .load = load_module,
2579 .unload = unload_module,
2581 .load_pri = AST_MODPRI_DEVSTATE_PROVIDER,