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, 1);
1513 if (logged != agent->logged) {
1514 /* This channel is no longer the logged in agent. */
1519 /* The agent is no longer configured. */
1523 /* Update the agent's config before rejoining the holding bridge. */
1524 cfgs = ao2_global_obj_ref(cfg_handle);
1526 /* There is no agent configuration. All agents were destroyed. */
1529 cfg_new = ao2_find(cfgs->agents, agent->username, OBJ_KEY);
1532 /* The agent is no longer configured. */
1536 cfg_old = agent->cfg;
1537 agent->cfg = cfg_new;
1539 agent->last_disconnect = ast_tvnow();
1541 /* Clear out any caller bridge before rejoining the holding bridge. */
1542 caller_bridge = agent->caller_bridge;
1543 agent->caller_bridge = NULL;
1544 agent_unlock(agent);
1545 ao2_ref(cfg_old, -1);
1546 if (caller_bridge) {
1547 ast_bridge_destroy(caller_bridge, AST_CAUSE_USER_BUSY);
1550 if (agent->state == AGENT_STATE_LOGGING_OUT
1551 || agent->deferred_logoff
1552 || ast_check_hangup_locked(logged)) {
1553 /* The agent was requested to logout or hungup. */
1558 * It is safe to access agent->waiting_colp without a lock. It
1559 * is only setup on agent login and not changed.
1561 ast_channel_update_connected_line(logged, &agent->waiting_colp, NULL);
1563 ast_bridge_features_cleanup(&features);
1567 if (logged != agent->logged) {
1569 * We are no longer the agent channel because of local channel
1572 agent_unlock(agent);
1573 ast_debug(1, "Agent %s: Channel %s is no longer the agent.\n",
1574 agent->username, ast_channel_name(logged));
1577 agent_logout(agent);
1580 static void agent_after_bridge_cb(struct ast_channel *chan, void *data)
1582 struct agent_pvt *agent;
1584 agent = ao2_find(agents, chan, 0);
1589 ast_debug(1, "Agent %s: New agent channel %s.\n",
1590 agent->username, ast_channel_name(chan));
1591 agent_run(agent, chan);
1595 static void agent_after_bridge_cb_failed(enum ast_bridge_after_cb_reason reason, void *data)
1597 struct ast_channel *chan = data;
1598 struct agent_pvt *agent;
1600 agent = ao2_find(agents, chan, 0);
1604 ast_log(LOG_WARNING, "Agent %s: Forced logout. Lost control of %s because: %s\n",
1605 agent->username, ast_channel_name(chan),
1606 ast_bridge_after_cb_reason_string(reason));
1608 agent_logout(agent);
1614 * \brief Get the lock on the agent bridge_channel and return it.
1617 * \param agent Whose bridge_channel to get.
1619 * \retval bridge_channel on success (Reffed and locked).
1620 * \retval NULL on error.
1622 static struct ast_bridge_channel *agent_bridge_channel_get_lock(struct agent_pvt *agent)
1624 struct ast_channel *logged;
1625 struct ast_bridge_channel *bc;
1629 logged = agent->logged;
1631 agent_unlock(agent);
1634 ast_channel_ref(logged);
1635 agent_unlock(agent);
1637 ast_channel_lock(logged);
1638 bc = ast_channel_get_bridge_channel(logged);
1639 ast_channel_unlock(logged);
1640 ast_channel_unref(logged);
1642 if (agent->logged != logged) {
1648 ast_bridge_channel_lock(bc);
1649 if (bc->chan != logged || agent->logged != logged) {
1650 ast_bridge_channel_unlock(bc);
1658 static void caller_abort_agent(struct agent_pvt *agent)
1660 struct ast_bridge_channel *logged;
1662 logged = agent_bridge_channel_get_lock(agent);
1664 struct ast_bridge *caller_bridge;
1666 ast_debug(1, "Agent '%s' no longer logged in.\n", agent->username);
1669 caller_bridge = agent->caller_bridge;
1670 agent->caller_bridge = NULL;
1671 agent_unlock(agent);
1672 if (caller_bridge) {
1673 ast_bridge_destroy(caller_bridge, AST_CAUSE_USER_BUSY);
1678 /* Kick the agent out of the holding bridge to reset it. */
1679 ast_bridge_channel_leave_bridge_nolock(logged, BRIDGE_CHANNEL_STATE_END,
1680 AST_CAUSE_NORMAL_CLEARING);
1681 ast_bridge_channel_unlock(logged);
1684 static int caller_safety_timeout(struct ast_bridge_channel *bridge_channel, void *hook_pvt)
1686 struct agent_pvt *agent = hook_pvt;
1688 if (agent->state == AGENT_STATE_CALL_PRESENT) {
1689 ast_verb(3, "Agent '%s' did not respond. Safety timeout.\n", agent->username);
1690 ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END,
1691 AST_CAUSE_USER_BUSY);
1692 caller_abort_agent(agent);
1698 static void agent_alert(struct ast_bridge_channel *bridge_channel, const void *payload, size_t payload_size)
1700 const char *agent_id = payload;
1701 const char *playfile;
1702 RAII_VAR(struct agent_pvt *, agent, NULL, ao2_cleanup);
1704 agent = ao2_find(agents, agent_id, OBJ_KEY);
1706 ast_debug(1, "Agent '%s' does not exist. Where did it go?\n", agent_id);
1710 /* Change holding bridge participant role's idle mode to silence */
1711 ast_bridge_channel_lock_bridge(bridge_channel);
1712 ast_bridge_channel_clear_roles(bridge_channel);
1713 ast_channel_set_bridge_role_option(bridge_channel->chan, "holding_participant", "idle_mode", "silence");
1714 ast_bridge_channel_establish_roles(bridge_channel);
1715 ast_bridge_unlock(bridge_channel->bridge);
1717 /* Alert the agent. */
1719 playfile = ast_strdupa(agent->cfg->beep_sound);
1720 agent_unlock(agent);
1721 ast_stream_and_wait(bridge_channel->chan, playfile, AST_DIGIT_NONE);
1724 switch (agent->state) {
1725 case AGENT_STATE_CALL_PRESENT:
1726 if (ast_test_flag(agent, AGENT_FLAG_ACK_CALL)
1727 ? agent->override_ack_call : agent->cfg->ack_call) {
1728 agent->state = AGENT_STATE_CALL_WAIT_ACK;
1729 agent->ack_time = ast_tvnow();
1733 /* Connect to caller now. */
1734 ast_debug(1, "Agent %s: Immediately connecting to call.\n", agent->username);
1735 agent_connect_caller(bridge_channel, agent);/* Will unlock agent. */
1740 agent_unlock(agent);
1743 static int send_alert_to_agent(struct ast_bridge_channel *bridge_channel, const char *agent_id)
1745 return ast_bridge_channel_queue_callback(bridge_channel,
1746 AST_BRIDGE_CHANNEL_CB_OPTION_MEDIA, agent_alert, agent_id, strlen(agent_id) + 1);
1749 static int send_colp_to_agent(struct ast_bridge_channel *bridge_channel, struct ast_party_connected_line *connected)
1751 struct ast_set_party_connected_line update = {
1756 unsigned char data[1024]; /* This should be large enough */
1759 datalen = ast_connected_line_build_data(data, sizeof(data), connected, &update);
1760 if (datalen == (size_t) -1) {
1764 return ast_bridge_channel_queue_control_data(bridge_channel,
1765 AST_CONTROL_CONNECTED_LINE, data, datalen);
1769 * \brief Dialplan AgentRequest application to locate an agent to talk with.
1771 * \param chan Channel wanting to talk with an agent.
1772 * \param data Application parameters
1774 * \retval 0 To continue in dialplan.
1775 * \retval -1 To hangup.
1777 static int agent_request_exec(struct ast_channel *chan, const char *data)
1779 struct ast_bridge *caller_bridge;
1780 struct ast_bridge_channel *logged;
1783 struct ast_bridge_features caller_features;
1784 struct ast_party_connected_line connected;
1785 AST_DECLARE_APP_ARGS(args,
1786 AST_APP_ARG(agent_id);
1787 AST_APP_ARG(other); /* Any remaining unused arguments */
1790 RAII_VAR(struct agent_pvt *, agent, NULL, ao2_cleanup);
1792 if (bridge_agent_hold_deferred_create()) {
1796 parse = ast_strdupa(data);
1797 AST_STANDARD_APP_ARGS(args, parse);
1799 if (ast_strlen_zero(args.agent_id)) {
1800 ast_log(LOG_WARNING, "AgentRequest requires an AgentId\n");
1804 /* Find the agent. */
1805 agent = ao2_find(agents, args.agent_id, OBJ_KEY);
1807 ast_verb(3, "Agent '%s' does not exist.\n", args.agent_id);
1808 pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "INVALID");
1812 if (ast_bridge_features_init(&caller_features)) {
1816 /* Add safety timeout hook. */
1818 if (ast_bridge_interval_hook(&caller_features, 0, CALLER_SAFETY_TIMEOUT_TIME,
1819 caller_safety_timeout, agent, __ao2_cleanup, AST_BRIDGE_HOOK_REMOVE_ON_PULL)) {
1821 ast_bridge_features_cleanup(&caller_features);
1825 caller_bridge = ast_bridge_basic_new();
1826 if (!caller_bridge) {
1827 ast_bridge_features_cleanup(&caller_features);
1831 /* Get COLP for agent. */
1832 ast_party_connected_line_init(&connected);
1833 ast_channel_lock(chan);
1834 ast_connected_line_copy_from_caller(&connected, ast_channel_caller(chan));
1835 ast_channel_unlock(chan);
1838 switch (agent->state) {
1839 case AGENT_STATE_LOGGED_OUT:
1840 case AGENT_STATE_LOGGING_OUT:
1841 agent_unlock(agent);
1842 ast_party_connected_line_free(&connected);
1843 ast_bridge_destroy(caller_bridge, 0);
1844 ast_bridge_features_cleanup(&caller_features);
1845 ast_verb(3, "Agent '%s' not logged in.\n", agent->username);
1846 pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "NOT_LOGGED_IN");
1848 case AGENT_STATE_READY_FOR_CALL:
1849 ao2_ref(caller_bridge, +1);
1850 agent->caller_bridge = caller_bridge;
1851 agent->state = AGENT_STATE_CALL_PRESENT;
1852 agent->devstate = AST_DEVICE_INUSE;
1855 agent_unlock(agent);
1856 ast_party_connected_line_free(&connected);
1857 ast_bridge_destroy(caller_bridge, 0);
1858 ast_bridge_features_cleanup(&caller_features);
1859 ast_verb(3, "Agent '%s' is busy.\n", agent->username);
1860 pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "BUSY");
1863 agent_unlock(agent);
1864 agent_devstate_changed(agent->username);
1866 logged = agent_bridge_channel_get_lock(agent);
1868 ast_party_connected_line_free(&connected);
1869 ast_bridge_destroy(caller_bridge, 0);
1870 ast_bridge_features_cleanup(&caller_features);
1871 ast_verb(3, "Agent '%s' not logged in.\n", agent->username);
1872 pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "NOT_LOGGED_IN");
1873 caller_abort_agent(agent);
1877 send_colp_to_agent(logged, &connected);
1878 ast_party_connected_line_free(&connected);
1880 res = send_alert_to_agent(logged, agent->username);
1881 ast_bridge_channel_unlock(logged);
1882 ao2_ref(logged, -1);
1884 ast_bridge_destroy(caller_bridge, 0);
1885 ast_bridge_features_cleanup(&caller_features);
1886 ast_verb(3, "Agent '%s': Failed to alert the agent.\n", agent->username);
1887 pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "ERROR");
1888 caller_abort_agent(agent);
1892 ast_indicate(chan, AST_CONTROL_RINGING);
1893 ast_bridge_join(caller_bridge, chan, NULL, &caller_features, NULL, 1);
1894 ast_bridge_features_cleanup(&caller_features);
1901 * \brief Get agent config values from the login channel.
1904 * \param agent What to setup channel config values on.
1905 * \param chan Channel logging in as an agent.
1909 static void agent_login_channel_config(struct agent_pvt *agent, struct ast_channel *chan)
1911 struct ast_flags opts = { 0 };
1912 struct ast_party_connected_line connected;
1913 unsigned int override_ack_call = 0;
1914 unsigned int override_auto_logoff = 0;
1915 unsigned int override_wrapup_time = 0;
1916 const char *override_dtmf_accept = NULL;
1919 ast_party_connected_line_init(&connected);
1921 /* Get config values from channel. */
1922 ast_channel_lock(chan);
1923 ast_party_connected_line_copy(&connected, ast_channel_connected(chan));
1925 var = pbx_builtin_getvar_helper(chan, "AGENTACKCALL");
1926 if (!ast_strlen_zero(var)) {
1927 override_ack_call = ast_true(var) ? 1 : 0;
1928 ast_set_flag(&opts, AGENT_FLAG_ACK_CALL);
1931 var = pbx_builtin_getvar_helper(chan, "AGENTACCEPTDTMF");
1932 if (!ast_strlen_zero(var)) {
1933 override_dtmf_accept = ast_strdupa(var);
1934 ast_set_flag(&opts, AGENT_FLAG_DTMF_ACCEPT);
1937 var = pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF");
1938 if (!ast_strlen_zero(var)) {
1939 if (sscanf(var, "%u", &override_auto_logoff) == 1) {
1940 ast_set_flag(&opts, AGENT_FLAG_AUTO_LOGOFF);
1944 var = pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME");
1945 if (!ast_strlen_zero(var)) {
1946 if (sscanf(var, "%u", &override_wrapup_time) == 1) {
1947 ast_set_flag(&opts, AGENT_FLAG_WRAPUP_TIME);
1950 ast_channel_unlock(chan);
1952 /* Set config values on agent. */
1954 ast_party_connected_line_free(&agent->waiting_colp);
1955 agent->waiting_colp = connected;
1957 ast_string_field_set(agent, override_dtmf_accept, override_dtmf_accept);
1958 ast_copy_flags(agent, &opts, AST_FLAGS_ALL);
1959 agent->override_auto_logoff = override_auto_logoff;
1960 agent->override_wrapup_time = override_wrapup_time;
1961 agent->override_ack_call = override_ack_call;
1962 agent_unlock(agent);
1965 enum AGENT_LOGIN_OPT_FLAGS {
1966 OPT_SILENT = (1 << 0),
1968 AST_APP_OPTIONS(agent_login_opts, BEGIN_OPTIONS
1969 AST_APP_OPTION('s', OPT_SILENT),
1973 * \brief Dialplan AgentLogin application to log in an agent.
1975 * \param chan Channel attempting to login as an agent.
1976 * \param data Application parameters
1978 * \retval 0 To continue in dialplan.
1979 * \retval -1 To hangup.
1981 static int agent_login_exec(struct ast_channel *chan, const char *data)
1984 struct ast_flags opts;
1985 AST_DECLARE_APP_ARGS(args,
1986 AST_APP_ARG(agent_id);
1987 AST_APP_ARG(options);
1988 AST_APP_ARG(other); /* Any remaining unused arguments */
1991 RAII_VAR(struct agent_pvt *, agent, NULL, ao2_cleanup);
1993 if (bridge_agent_hold_deferred_create()) {
1997 if (ast_channel_state(chan) != AST_STATE_UP && ast_answer(chan)) {
2001 parse = ast_strdupa(data);
2002 AST_STANDARD_APP_ARGS(args, parse);
2004 if (ast_strlen_zero(args.agent_id)) {
2005 ast_log(LOG_WARNING, "AgentLogin requires an AgentId\n");
2009 if (ast_app_parse_options(agent_login_opts, &opts, NULL, args.options)) {
2010 /* General invalid option syntax. */
2014 /* Find the agent. */
2015 agent = ao2_find(agents, args.agent_id, OBJ_KEY);
2017 ast_verb(3, "Agent '%s' does not exist.\n", args.agent_id);
2018 pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "INVALID");
2022 /* Has someone already logged in as this agent already? */
2024 if (agent->logged) {
2025 agent_unlock(agent);
2026 ast_verb(3, "Agent '%s' already logged in.\n", agent->username);
2027 pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "ALREADY_LOGGED_IN");
2030 agent->logged = ast_channel_ref(chan);
2031 agent->last_disconnect = ast_tvnow();
2032 time(&agent->login_start);
2033 agent_unlock(agent);
2035 agent_login_channel_config(agent, chan);
2037 if (!ast_test_flag(&opts, OPT_SILENT)
2038 && !ast_streamfile(chan, "agent-loginok", ast_channel_language(chan))) {
2039 ast_waitstream(chan, "");
2042 ast_verb(2, "Agent '%s' logged in (format %s/%s)\n", agent->username,
2043 ast_getformatname(ast_channel_readformat(chan)),
2044 ast_getformatname(ast_channel_writeformat(chan)));
2045 send_agent_login(chan, agent->username);
2047 agent_run(agent, chan);
2051 static int agent_function_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
2054 struct agent_pvt *agent;
2055 struct ast_channel *logged;
2056 AST_DECLARE_APP_ARGS(args,
2057 AST_APP_ARG(agentid);
2063 parse = ast_strdupa(data ?: "");
2064 AST_NONSTANDARD_APP_ARGS(args, parse, ':');
2066 if (ast_strlen_zero(args.agentid)) {
2067 ast_log(LOG_WARNING, "The AGENT function requires an argument - agentid!\n");
2071 args.item = "status";
2074 agent = ao2_find(agents, args.agentid, OBJ_KEY);
2076 ast_log(LOG_WARNING, "Agent '%s' not found!\n", args.agentid);
2081 if (!strcasecmp(args.item, "status")) {
2084 if (agent->logged) {
2085 status = "LOGGEDIN";
2087 status = "LOGGEDOUT";
2089 ast_copy_string(buf, status, len);
2090 } else if (!strcasecmp(args.item, "name")) {
2091 ast_copy_string(buf, agent->cfg->full_name, len);
2092 } else if (!strcasecmp(args.item, "mohclass")) {
2093 ast_copy_string(buf, agent->cfg->moh, len);
2094 } else if (!strcasecmp(args.item, "channel")) {
2095 logged = agent_lock_logged(agent);
2099 ast_copy_string(buf, ast_channel_name(logged), len);
2100 ast_channel_unlock(logged);
2101 ast_channel_unref(logged);
2103 pos = strrchr(buf, '-');
2108 } else if (!strcasecmp(args.item, "fullchannel")) {
2109 logged = agent_lock_logged(agent);
2111 ast_copy_string(buf, ast_channel_name(logged), len);
2112 ast_channel_unlock(logged);
2113 ast_channel_unref(logged);
2116 agent_unlock(agent);
2122 static struct ast_custom_function agent_function = {
2124 .read = agent_function_read,
2127 struct agent_complete {
2128 /*! Nth match to return. */
2130 /*! Which match currently on. */
2134 static int complete_agent_search(void *obj, void *arg, void *data, int flags)
2136 struct agent_complete *search = data;
2138 if (++search->which > search->state) {
2144 static char *complete_agent(const char *word, int state)
2147 struct agent_pvt *agent;
2148 struct agent_complete search = {
2152 agent = ao2_callback_data(agents, ast_strlen_zero(word) ? 0 : OBJ_PARTIAL_KEY,
2153 complete_agent_search, (char *) word, &search);
2157 ret = ast_strdup(agent->username);
2162 static int complete_agent_logoff_search(void *obj, void *arg, void *data, int flags)
2164 struct agent_pvt *agent = obj;
2165 struct agent_complete *search = data;
2167 if (!agent->logged) {
2170 if (++search->which > search->state) {
2176 static char *complete_agent_logoff(const char *word, int state)
2179 struct agent_pvt *agent;
2180 struct agent_complete search = {
2184 agent = ao2_callback_data(agents, ast_strlen_zero(word) ? 0 : OBJ_PARTIAL_KEY,
2185 complete_agent_logoff_search, (char *) word, &search);
2189 ret = ast_strdup(agent->username);
2194 static void agent_show_requested(struct ast_cli_args *a, int online_only)
2196 #define FORMAT_HDR "%-8s %-20s %-11s %-30s %s\n"
2197 #define FORMAT_ROW "%-8s %-20s %-11s %-30s %s\n"
2199 struct ao2_iterator iter;
2200 struct agent_pvt *agent;
2201 struct ast_str *out = ast_str_alloca(512);
2202 unsigned int agents_total = 0;
2203 unsigned int agents_logged_in = 0;
2204 unsigned int agents_talking = 0;
2206 ast_cli(a->fd, FORMAT_HDR, "Agent-ID", "Name", "State", "Channel", "Talking with");
2207 iter = ao2_iterator_init(agents, 0);
2208 for (; (agent = ao2_iterator_next(&iter)); ao2_ref(agent, -1)) {
2209 struct ast_channel *logged;
2214 logged = agent_lock_logged(agent);
2216 const char *talking_with;
2220 talking_with = pbx_builtin_getvar_helper(logged, "BRIDGEPEER");
2221 if (!ast_strlen_zero(talking_with)) {
2226 ast_str_set(&out, 0, FORMAT_ROW, agent->username, agent->cfg->full_name,
2227 ast_devstate_str(agent->devstate), ast_channel_name(logged), talking_with);
2228 ast_channel_unlock(logged);
2229 ast_channel_unref(logged);
2231 ast_str_set(&out, 0, FORMAT_ROW, agent->username, agent->cfg->full_name,
2232 ast_devstate_str(agent->devstate), "", "");
2234 agent_unlock(agent);
2236 if (!online_only || logged) {
2237 ast_cli(a->fd, "%s", ast_str_buffer(out));
2240 ao2_iterator_destroy(&iter);
2242 ast_cli(a->fd, "\nDefined agents: %u, Logged in: %u, Talking: %u\n",
2243 agents_total, agents_logged_in, agents_talking);
2249 static char *agent_handle_show_online(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2253 e->command = "agent show online";
2255 "Usage: agent show online\n"
2256 " Provides summary information for logged in agents.\n";
2263 return CLI_SHOWUSAGE;
2266 agent_show_requested(a, 1);
2271 static char *agent_handle_show_all(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2275 e->command = "agent show all";
2277 "Usage: agent show all\n"
2278 " Provides summary information for all agents.\n";
2285 return CLI_SHOWUSAGE;
2288 agent_show_requested(a, 0);
2293 static char *agent_handle_show_specific(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2295 struct agent_pvt *agent;
2296 struct ast_channel *logged;
2297 struct ast_str *out = ast_str_alloca(4096);
2301 e->command = "agent show";
2303 "Usage: agent show <agent-id>\n"
2304 " Show information about the <agent-id> agent\n";
2308 return complete_agent(a->word, a->n);
2314 return CLI_SHOWUSAGE;
2317 agent = ao2_find(agents, a->argv[2], OBJ_KEY);
2319 ast_cli(a->fd, "Agent '%s' not found\n", a->argv[2]);
2324 logged = agent_lock_logged(agent);
2325 ast_str_set(&out, 0, "Id: %s\n", agent->username);
2326 ast_str_append(&out, 0, "Name: %s\n", agent->cfg->full_name);
2327 ast_str_append(&out, 0, "Beep: %s\n", agent->cfg->beep_sound);
2328 ast_str_append(&out, 0, "MOH: %s\n", agent->cfg->moh);
2329 ast_str_append(&out, 0, "RecordCalls: %s\n", AST_CLI_YESNO(agent->cfg->record_agent_calls));
2330 ast_str_append(&out, 0, "State: %s\n", ast_devstate_str(agent->devstate));
2332 const char *talking_with;
2334 ast_str_append(&out, 0, "LoggedInChannel: %s\n", ast_channel_name(logged));
2335 ast_str_append(&out, 0, "LoggedInTime: %ld\n", (long) agent->login_start);
2336 talking_with = pbx_builtin_getvar_helper(logged, "BRIDGEPEER");
2337 if (!ast_strlen_zero(talking_with)) {
2338 ast_str_append(&out, 0, "TalkingWith: %s\n", talking_with);
2339 ast_str_append(&out, 0, "CallStarted: %ld\n", (long) agent->call_start);
2341 ast_channel_unlock(logged);
2342 ast_channel_unref(logged);
2344 agent_unlock(agent);
2347 ast_cli(a->fd, "%s", ast_str_buffer(out));
2352 static char *agent_handle_logoff_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2356 e->command = "agent logoff";
2358 "Usage: agent logoff <agent-id> [soft]\n"
2359 " Sets an agent as no longer logged in.\n"
2360 " If 'soft' is specified, do not hangup existing calls.\n";
2364 return complete_agent_logoff(a->word, a->n);
2365 } else if (a->pos == 3 && a->n == 0
2366 && (ast_strlen_zero(a->word)
2367 || !strncasecmp("soft", a->word, strlen(a->word)))) {
2368 return ast_strdup("soft");
2373 if (a->argc < 3 || 4 < a->argc) {
2374 return CLI_SHOWUSAGE;
2376 if (a->argc == 4 && strcasecmp(a->argv[3], "soft")) {
2377 return CLI_SHOWUSAGE;
2380 if (!agent_logoff_request(a->argv[2], a->argc == 4)) {
2381 ast_cli(a->fd, "Logging out %s\n", a->argv[2]);
2387 static struct ast_cli_entry cli_agents[] = {
2388 AST_CLI_DEFINE(agent_handle_show_online, "Show status of online agents"),
2389 AST_CLI_DEFINE(agent_handle_show_all, "Show status of all agents"),
2390 AST_CLI_DEFINE(agent_handle_show_specific, "Show information about an agent"),
2391 AST_CLI_DEFINE(agent_handle_logoff_cmd, "Sets an agent offline"),
2394 static int action_agents(struct mansession *s, const struct message *m)
2396 const char *id = astman_get_header(m, "ActionID");
2397 char id_text[AST_MAX_BUF];
2398 struct ao2_iterator iter;
2399 struct agent_pvt *agent;
2400 struct ast_str *out = ast_str_alloca(4096);
2402 if (!ast_strlen_zero(id)) {
2403 snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", id);
2407 astman_send_ack(s, m, "Agents will follow");
2409 iter = ao2_iterator_init(agents, 0);
2410 for (; (agent = ao2_iterator_next(&iter)); ao2_ref(agent, -1)) {
2411 struct ast_channel *logged;
2414 logged = agent_lock_logged(agent);
2418 * AGENT_LOGGEDOFF - Agent isn't logged in
2419 * AGENT_IDLE - Agent is logged in, and waiting for call
2420 * AGENT_ONCALL - Agent is logged in, and on a call
2421 * AGENT_UNKNOWN - Don't know anything about agent. Shouldn't ever get this.
2423 ast_str_set(&out, 0, "Agent: %s\r\n", agent->username);
2424 ast_str_append(&out, 0, "Name: %s\r\n", agent->cfg->full_name);
2427 const char *talking_to_chan;
2428 struct ast_str *logged_headers;
2429 RAII_VAR(struct ast_channel_snapshot *, logged_snapshot, ast_channel_snapshot_create(logged), ao2_cleanup);
2431 if (!logged_snapshot
2432 || !(logged_headers =
2433 ast_manager_build_channel_state_string(logged_snapshot))) {
2434 ast_channel_unlock(logged);
2435 ast_channel_unref(logged);
2436 agent_unlock(agent);
2440 talking_to_chan = pbx_builtin_getvar_helper(logged, "BRIDGEPEER");
2441 if (!ast_strlen_zero(talking_to_chan)) {
2442 ast_str_append(&out, 0, "Status: %s\r\n", "AGENT_ONCALL");
2443 ast_str_append(&out, 0, "TalkingToChan: %s\r\n", talking_to_chan);
2444 ast_str_append(&out, 0, "CallStarted: %ld\n", (long) agent->call_start);
2446 ast_str_append(&out, 0, "Status: %s\r\n", "AGENT_IDLE");
2448 ast_str_append(&out, 0, "LoggedInTime: %ld\r\n", (long) agent->login_start);
2449 ast_str_append(&out, 0, "%s", ast_str_buffer(logged_headers));
2450 ast_channel_unlock(logged);
2451 ast_channel_unref(logged);
2452 ast_free(logged_headers);
2454 ast_str_append(&out, 0, "Status: %s\r\n", "AGENT_LOGGEDOFF");
2457 agent_unlock(agent);
2459 astman_append(s, "Event: Agents\r\n"
2461 ast_str_buffer(out), id_text);
2463 ao2_iterator_destroy(&iter);
2465 astman_append(s, "Event: AgentsComplete\r\n"
2471 static int action_agent_logoff(struct mansession *s, const struct message *m)
2473 const char *agent = astman_get_header(m, "Agent");
2474 const char *soft_s = astman_get_header(m, "Soft"); /* "true" is don't hangup */
2476 if (ast_strlen_zero(agent)) {
2477 astman_send_error(s, m, "No agent specified");
2481 if (!agent_logoff_request(agent, ast_true(soft_s))) {
2482 astman_send_ack(s, m, "Agent logged out");
2484 astman_send_error(s, m, "No such agent");
2490 static int unload_module(void)
2492 struct ast_bridge *holding;
2494 /* Unregister dialplan applications */
2495 ast_unregister_application(app_agent_login);
2496 ast_unregister_application(app_agent_request);
2498 /* Unregister dialplan functions */
2499 ast_custom_function_unregister(&agent_function);
2501 /* Unregister manager command */
2502 ast_manager_unregister("Agents");
2503 ast_manager_unregister("AgentLogoff");
2505 /* Unregister CLI commands */
2506 ast_cli_unregister_multiple(cli_agents, ARRAY_LEN(cli_agents));
2508 ast_devstate_prov_del("Agent");
2510 /* Destroy agent holding bridge. */
2511 holding = ao2_global_obj_replace(agent_holding, NULL);
2513 ast_bridge_destroy(holding, 0);
2517 ao2_ref(agents, -1);
2522 static int load_module(void)
2526 agents = ao2_container_alloc_rbtree(AO2_ALLOC_OPT_LOCK_MUTEX,
2527 AO2_CONTAINER_ALLOC_OPT_DUPS_REPLACE, agent_pvt_sort_cmp, agent_pvt_cmp);
2529 return AST_MODULE_LOAD_FAILURE;
2531 if (load_config()) {
2532 ast_log(LOG_ERROR, "Unable to load config. Not loading module.\n");
2533 ao2_ref(agents, -1);
2535 return AST_MODULE_LOAD_DECLINE;
2538 /* Init agent holding bridge v_table. */
2539 bridge_init_agent_hold();
2541 /* Setup to provide Agent:agent-id device state. */
2542 res |= ast_devstate_prov_add("Agent", agent_pvt_devstate_get);
2545 res |= ast_cli_register_multiple(cli_agents, ARRAY_LEN(cli_agents));
2547 /* Manager commands */
2548 res |= ast_manager_register_xml("Agents", EVENT_FLAG_AGENT, action_agents);
2549 res |= ast_manager_register_xml("AgentLogoff", EVENT_FLAG_AGENT, action_agent_logoff);
2551 /* Dialplan Functions */
2552 res |= ast_custom_function_register(&agent_function);
2554 /* Dialplan applications */
2555 res |= ast_register_application_xml(app_agent_login, agent_login_exec);
2556 res |= ast_register_application_xml(app_agent_request, agent_request_exec);
2560 return AST_MODULE_LOAD_FAILURE;
2562 return AST_MODULE_LOAD_SUCCESS;
2565 static int reload(void)
2567 if (aco_process_config(&cfg_info, 1) == ACO_PROCESS_ERROR) {
2568 /* Just keep the config we already have in place. */
2574 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Call center agent pool applications",
2575 .load = load_module,
2576 .unload = unload_module,
2578 .load_pri = AST_MODPRI_DEVSTATE_PROVIDER,