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/bridging.h"
44 #include "asterisk/bridging_internal.h"
45 #include "asterisk/bridging_basic.h"
46 #include "asterisk/bridging_after.h"
47 #include "asterisk/config_options.h"
48 #include "asterisk/features_config.h"
49 #include "asterisk/astobj2.h"
50 #include "asterisk/stringfields.h"
51 #include "asterisk/stasis_channels.h"
54 <application name="AgentLogin" language="en_US">
59 <parameter name="AgentId" required="true" />
60 <parameter name="options">
63 <para>silent login - do not announce the login ok segment after
64 agent logged on.</para>
70 <para>Login an agent to the system. Any agent authentication is assumed to
71 already be done by dialplan. While logged in, the agent can receive calls
72 and will hear the sound file specified by the config option custom_beep
73 when a new call comes in for the agent. Login failures will continue in
74 the dialplan with <variable>AGENT_STATUS</variable> set.</para>
75 <para>Before logging in, you can setup on the real agent channel the
76 CHANNEL(dtmf-features) an agent will have when talking to a caller
77 and you can setup on the channel running this application the
78 CONNECTEDLINE() information the agent will see while waiting for a
80 <para><variable>AGENT_STATUS</variable> enumeration values:</para>
82 <enum name = "INVALID"><para>The specified agent is invalid.</para></enum>
83 <enum name = "ALREADY_LOGGED_IN"><para>The agent is already logged in.</para></enum>
85 <note><para>The Agents:<replaceable>AgentId</replaceable> device state is
86 available to monitor the status of the agent.</para></note>
89 <ref type="application">Authenticate</ref>
90 <ref type="application">Queue</ref>
91 <ref type="application">AddQueueMember</ref>
92 <ref type="application">RemoveQueueMember</ref>
93 <ref type="application">PauseQueueMember</ref>
94 <ref type="application">UnpauseQueueMember</ref>
95 <ref type="function">AGENT</ref>
96 <ref type="function">CHANNEL(dtmf-features)</ref>
97 <ref type="function">CONNECTEDLINE()</ref>
98 <ref type="filename">agents.conf</ref>
99 <ref type="filename">queues.conf</ref>
102 <application name="AgentRequest" language="en_US">
104 Request an agent to connect with the channel.
107 <parameter name="AgentId" required="true" />
110 <para>Request an agent to connect with the channel. Failure to find and
111 alert an agent will continue in the dialplan with <variable>AGENT_STATUS</variable> set.</para>
112 <para><variable>AGENT_STATUS</variable> enumeration values:</para>
114 <enum name = "INVALID"><para>The specified agent is invalid.</para></enum>
115 <enum name = "NOT_LOGGED_IN"><para>The agent is not available.</para></enum>
116 <enum name = "BUSY"><para>The agent is on another call.</para></enum>
117 <enum name = "ERROR"><para>Alerting the agent failed.</para></enum>
121 <ref type="application">AgentLogin</ref>
124 <function name="AGENT" language="en_US">
126 Gets information about an Agent
129 <parameter name="AgentId" required="true" />
130 <parameter name="item">
131 <para>The valid items to retrieve are:</para>
134 <para>(default) The status of the agent (LOGGEDIN | LOGGEDOUT)</para>
136 <enum name="password">
137 <para>Deprecated. The dialplan handles any agent authentication.</para>
140 <para>The name of the agent</para>
142 <enum name="mohclass">
143 <para>MusicOnHold class</para>
145 <enum name="channel">
146 <para>The name of the active channel for the Agent (AgentLogin)</para>
148 <enum name="fullchannel">
149 <para>The untruncated name of the active channel for the Agent (AgentLogin)</para>
154 <description></description>
156 <manager name="Agents" language="en_US">
158 Lists agents and their status.
161 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
164 <para>Will list info about all defined agents.</para>
167 <ref type="managerEvent">Agents</ref>
168 <ref type="managerEvent">AgentsComplete</ref>
171 <managerEvent language="en_US" name="Agents">
172 <managerEventInstance class="EVENT_FLAG_AGENT">
174 Response event in a series to the Agents AMI action containing
175 information about a defined agent.
178 <parameter name="Agent">
179 <para>Agent ID of the agent.</para>
181 <parameter name="Name">
182 <para>User friendly name of the agent.</para>
184 <parameter name="Status">
185 <para>Current status of the agent.</para>
186 <para>The valid values are:</para>
188 <enum name="AGENT_LOGGEDOFF" />
189 <enum name="AGENT_IDLE" />
190 <enum name="AGENT_ONCALL" />
193 <parameter name="TalkingToChan">
194 <para><variable>BRIDGEPEER</variable> value on agent channel.</para>
195 <para>Present if Status value is <literal>AGENT_ONCALL</literal>.</para>
197 <parameter name="CallStarted">
198 <para>Epoche time when the agent started talking with the caller.</para>
199 <para>Present if Status value is <literal>AGENT_ONCALL</literal>.</para>
201 <parameter name="LoggedInTime">
202 <para>Epoche time when the agent logged in.</para>
203 <para>Present if Status value is <literal>AGENT_IDLE</literal> or <literal>AGENT_ONCALL</literal>.</para>
205 <parameter name="Channel">
206 <xi:include xpointer="xpointer(/docs/managerEvent[@name='Newchannel']/managerEventInstance/syntax/parameter[@name='Channel']/para)" />
207 <para>Present if Status value is <literal>AGENT_IDLE</literal> or <literal>AGENT_ONCALL</literal>.</para>
209 <parameter name="ChannelState">
210 <xi:include xpointer="xpointer(/docs/managerEvent[@name='Newchannel']/managerEventInstance/syntax/parameter[@name='ChannelState']/para)" />
211 <para>Present if Status value is <literal>AGENT_IDLE</literal> or <literal>AGENT_ONCALL</literal>.</para>
213 <parameter name="ChannelStateDesc">
214 <xi:include xpointer="xpointer(/docs/managerEvent[@name='Newchannel']/managerEventInstance/syntax/parameter[@name='ChannelStateDesc']/para)" />
215 <xi:include xpointer="xpointer(/docs/managerEvent[@name='Newchannel']/managerEventInstance/syntax/parameter[@name='ChannelStateDesc']/enumlist)" />
216 <para>Present if Status value is <literal>AGENT_IDLE</literal> or <literal>AGENT_ONCALL</literal>.</para>
218 <parameter name="CallerIDNum">
219 <para>Present if Status value is <literal>AGENT_IDLE</literal> or <literal>AGENT_ONCALL</literal>.</para>
221 <parameter name="CallerIDName">
222 <para>Present if Status value is <literal>AGENT_IDLE</literal> or <literal>AGENT_ONCALL</literal>.</para>
224 <parameter name="ConnectedLineNum">
225 <para>Present if Status value is <literal>AGENT_IDLE</literal> or <literal>AGENT_ONCALL</literal>.</para>
227 <parameter name="ConnectedLineName">
228 <para>Present if Status value is <literal>AGENT_IDLE</literal> or <literal>AGENT_ONCALL</literal>.</para>
230 <parameter name="AccountCode">
231 <para>Present if Status value is <literal>AGENT_IDLE</literal> or <literal>AGENT_ONCALL</literal>.</para>
233 <parameter name="Context">
234 <para>Present if Status value is <literal>AGENT_IDLE</literal> or <literal>AGENT_ONCALL</literal>.</para>
236 <parameter name="Exten">
237 <para>Present if Status value is <literal>AGENT_IDLE</literal> or <literal>AGENT_ONCALL</literal>.</para>
239 <parameter name="Priority">
240 <para>Present if Status value is <literal>AGENT_IDLE</literal> or <literal>AGENT_ONCALL</literal>.</para>
242 <parameter name="Uniqueid">
243 <xi:include xpointer="xpointer(/docs/managerEvent[@name='Newchannel']/managerEventInstance/syntax/parameter[@name='Uniqueid']/para)" />
244 <para>Present if Status value is <literal>AGENT_IDLE</literal> or <literal>AGENT_ONCALL</literal>.</para>
246 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
249 <ref type="manager">Agents</ref>
251 </managerEventInstance>
253 <managerEvent language="en_US" name="AgentsComplete">
254 <managerEventInstance class="EVENT_FLAG_AGENT">
256 Final response event in a series of events to the Agents AMI action.
259 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
262 <ref type="manager">Agents</ref>
264 </managerEventInstance>
266 <manager name="AgentLogoff" language="en_US">
268 Sets an agent as no longer logged in.
271 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
272 <parameter name="Agent" required="true">
273 <para>Agent ID of the agent to log off.</para>
275 <parameter name="Soft">
276 <para>Set to <literal>true</literal> to not hangup existing calls.</para>
280 <para>Sets an agent as no longer logged in.</para>
283 <configInfo name="app_agent_pool" language="en_US">
284 <synopsis>Agent pool applications</synopsis>
286 <note><para>Option changes take effect on agent login or after an agent
287 disconnects from a call.</para></note>
289 <configFile name="agents.conf">
290 <configObject name="global">
291 <synopsis>Unused, but reserved.</synopsis>
293 <configObject name="agent-id">
294 <synopsis>Configure an agent for the pool.</synopsis>
296 <xi:include xpointer="xpointer(/docs/configInfo[@name='app_agent_pool']/description/note)" />
298 <configOption name="ackcall">
299 <synopsis>Enable to require the agent to acknowledge a call.</synopsis>
301 <para>Enable to require the agent to give a DTMF acknowledgement
302 when the agent receives a call.</para>
303 <note><para>The option is overridden by <variable>AGENTACKCALL</variable> on agent login.</para></note>
304 <xi:include xpointer="xpointer(/docs/configInfo[@name='app_agent_pool']/description/note)" />
307 <configOption name="acceptdtmf">
308 <synopsis>DTMF key sequence the agent uses to acknowledge a call.</synopsis>
310 <note><para>The option is overridden by <variable>AGENTACCEPTDTMF</variable> on agent login.</para></note>
311 <note><para>The option is ignored unless the ackcall option is enabled.</para></note>
312 <xi:include xpointer="xpointer(/docs/configInfo[@name='app_agent_pool']/description/note)" />
315 <configOption name="autologoff">
316 <synopsis>Time the agent has to acknowledge a call before being logged off.</synopsis>
318 <para>Set how many seconds a call for the agent has to wait for the
319 agent to acknowledge the call before the agent is automatically
320 logged off. If set to zero then the call will wait forever for
321 the agent to acknowledge.</para>
322 <note><para>The option is overridden by <variable>AGENTAUTOLOGOFF</variable> on agent login.</para></note>
323 <note><para>The option is ignored unless the ackcall option is enabled.</para></note>
324 <xi:include xpointer="xpointer(/docs/configInfo[@name='app_agent_pool']/description/note)" />
327 <configOption name="wrapuptime">
328 <synopsis>Minimum time the agent has between calls.</synopsis>
330 <para>Set the minimum amount of time in milliseconds after
331 disconnecting a call before the agent can receive a new call.</para>
332 <note><para>The option is overridden by <variable>AGENTWRAPUPTIME</variable> on agent login.</para></note>
333 <xi:include xpointer="xpointer(/docs/configInfo[@name='app_agent_pool']/description/note)" />
336 <configOption name="musiconhold">
337 <synopsis>Music on hold class the agent listens to between calls.</synopsis>
339 <xi:include xpointer="xpointer(/docs/configInfo[@name='app_agent_pool']/description/note)" />
342 <configOption name="recordagentcalls">
343 <synopsis>Enable to automatically record calls the agent takes.</synopsis>
345 <para>Enable recording calls the agent takes automatically by
346 invoking the automixmon DTMF feature when the agent connects
347 to a caller. See <filename>features.conf.sample</filename> for information about
348 the automixmon feature.</para>
349 <xi:include xpointer="xpointer(/docs/configInfo[@name='app_agent_pool']/description/note)" />
352 <configOption name="custom_beep">
353 <synopsis>Sound file played to alert the agent when a call is present.</synopsis>
355 <xi:include xpointer="xpointer(/docs/configInfo[@name='app_agent_pool']/description/note)" />
358 <configOption name="fullname">
359 <synopsis>A friendly name for the agent used in log messages.</synopsis>
361 <xi:include xpointer="xpointer(/docs/configInfo[@name='app_agent_pool']/description/note)" />
369 /* ------------------------------------------------------------------- */
371 #define AST_MAX_BUF 256
373 /*! Maximum wait time (in ms) for the custom_beep file to play announcing the caller. */
374 #define CALLER_SAFETY_TIMEOUT_TIME (2 * 60 * 1000)
376 /*! Number of seconds to wait for local channel optimizations to complete. */
377 #define LOGIN_WAIT_TIMEOUT_TIME 5
379 static const char app_agent_login[] = "AgentLogin";
380 static const char app_agent_request[] = "AgentRequest";
382 /*! Agent config parameters. */
384 AST_DECLARE_STRING_FIELDS(
385 /*! Identification of the agent. (agents config container key) */
386 AST_STRING_FIELD(username);
387 /*! Name of agent for logging and querying purposes */
388 AST_STRING_FIELD(full_name);
391 * \brief DTMF string for an agent to accept a call.
393 * \note The channel variable AGENTACCEPTDTMF overrides on login.
395 AST_STRING_FIELD(dtmf_accept);
396 /*! Beep sound file to use. Alert the agent a call is waiting. */
397 AST_STRING_FIELD(beep_sound);
398 /*! MOH class to use while agent waiting for call. */
399 AST_STRING_FIELD(moh);
402 * \brief Number of seconds for agent to ack a call before being logged off.
404 * \note The channel variable AGENTAUTOLOGOFF overrides on login.
405 * \note If zero then timer is disabled.
407 unsigned int auto_logoff;
409 * \brief Time after a call in ms before the agent can get a new call.
411 * \note The channel variable AGENTWRAPUPTIME overrides on login.
413 unsigned int wrapup_time;
415 * \brief TRUE if agent needs to ack a call to accept it.
417 * \note The channel variable AGENTACKCALL overrides on login.
420 /*! TRUE if agent calls are automatically recorded. */
421 int record_agent_calls;
426 * \brief Agent config ao2 container sort function.
429 * \param obj_left pointer to the (user-defined part) of an object.
430 * \param obj_right pointer to the (user-defined part) of an object.
431 * \param flags flags from ao2_callback()
432 * OBJ_POINTER - if set, 'obj_right', is an object.
433 * OBJ_KEY - if set, 'obj_right', is a search key item that is not an object.
434 * OBJ_PARTIAL_KEY - if set, 'obj_right', is a partial search key item that is not an object.
436 * \retval <0 if obj_left < obj_right
437 * \retval =0 if obj_left == obj_right
438 * \retval >0 if obj_left > obj_right
440 static int agent_cfg_sort_cmp(const void *obj_left, const void *obj_right, int flags)
442 const struct agent_cfg *cfg_left = obj_left;
443 const struct agent_cfg *cfg_right = obj_right;
444 const char *right_key = obj_right;
447 switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
450 right_key = cfg_right->username;
453 cmp = strcmp(cfg_left->username, right_key);
455 case OBJ_PARTIAL_KEY:
456 cmp = strncmp(cfg_left->username, right_key, strlen(right_key));
462 static void agent_cfg_destructor(void *vdoomed)
464 struct agent_cfg *doomed = vdoomed;
466 ast_string_field_free_memory(doomed);
469 static void *agent_cfg_alloc(const char *name)
471 struct agent_cfg *cfg;
473 cfg = ao2_alloc_options(sizeof(*cfg), agent_cfg_destructor,
474 AO2_ALLOC_OPT_LOCK_NOLOCK);
475 if (!cfg || ast_string_field_init(cfg, 64)) {
478 ast_string_field_set(cfg, username, name);
482 static void *agent_cfg_find(struct ao2_container *agents, const char *username)
484 return ao2_find(agents, username, OBJ_KEY);
487 /*! Agents configuration */
489 /*! Master configured agents container. */
490 struct ao2_container *agents;
493 static struct aco_type agent_type = {
496 .category_match = ACO_BLACKLIST,
497 .category = "^(general|agents)$",
498 .item_alloc = agent_cfg_alloc,
499 .item_find = agent_cfg_find,
500 .item_offset = offsetof(struct agents_cfg, agents),
503 static struct aco_type *agent_types[] = ACO_TYPES(&agent_type);
505 /* The general category is reserved, but unused */
506 static struct aco_type general_type = {
509 .category_match = ACO_WHITELIST,
510 .category = "^general$",
513 static struct aco_file agents_conf = {
514 .filename = "agents.conf",
515 .types = ACO_TYPES(&general_type, &agent_type),
518 static AO2_GLOBAL_OBJ_STATIC(cfg_handle);
520 static void agents_cfg_destructor(void *vdoomed)
522 struct agents_cfg *doomed = vdoomed;
524 ao2_cleanup(doomed->agents);
525 doomed->agents = NULL;
530 * \brief Create struct agents_cfg object.
533 * \note A lock is not needed for the object or any secondary
534 * created cfg objects. These objects are immutable after the
535 * config is loaded and applied.
537 * \retval New struct agents_cfg object.
538 * \retval NULL on error.
540 static void *agents_cfg_alloc(void)
542 struct agents_cfg *cfg;
544 cfg = ao2_alloc_options(sizeof(*cfg), agents_cfg_destructor,
545 AO2_ALLOC_OPT_LOCK_NOLOCK);
549 cfg->agents = ao2_container_alloc_rbtree(AO2_ALLOC_OPT_LOCK_NOLOCK,
550 AO2_CONTAINER_ALLOC_OPT_DUPS_REJECT, agent_cfg_sort_cmp, NULL);
558 static void agents_post_apply_config(void);
560 CONFIG_INFO_STANDARD(cfg_info, cfg_handle, agents_cfg_alloc,
561 .files = ACO_FILES(&agents_conf),
562 .post_apply_config = agents_post_apply_config,
565 static void destroy_config(void)
567 ao2_global_obj_release(cfg_handle);
568 aco_info_destroy(&cfg_info);
571 static int load_config(void)
573 if (aco_info_init(&cfg_info)) {
578 aco_option_register(&cfg_info, "ackcall", ACO_EXACT, agent_types, "no", OPT_BOOL_T, 1, FLDSET(struct agent_cfg, ack_call));
579 aco_option_register(&cfg_info, "acceptdtmf", ACO_EXACT, agent_types, "#", OPT_STRINGFIELD_T, 1, STRFLDSET(struct agent_cfg, dtmf_accept));
580 aco_option_register(&cfg_info, "autologoff", ACO_EXACT, agent_types, "0", OPT_UINT_T, 0, FLDSET(struct agent_cfg, auto_logoff));
581 aco_option_register(&cfg_info, "wrapuptime", ACO_EXACT, agent_types, "0", OPT_UINT_T, 0, FLDSET(struct agent_cfg, wrapup_time));
582 aco_option_register(&cfg_info, "musiconhold", ACO_EXACT, agent_types, "default", OPT_STRINGFIELD_T, 0, STRFLDSET(struct agent_cfg, moh));
583 aco_option_register(&cfg_info, "recordagentcalls", ACO_EXACT, agent_types, "no", OPT_BOOL_T, 1, FLDSET(struct agent_cfg, record_agent_calls));
584 aco_option_register(&cfg_info, "custom_beep", ACO_EXACT, agent_types, "beep", OPT_STRINGFIELD_T, 1, STRFLDSET(struct agent_cfg, beep_sound));
585 aco_option_register(&cfg_info, "fullname", ACO_EXACT, agent_types, NULL, OPT_STRINGFIELD_T, 0, STRFLDSET(struct agent_cfg, full_name));
587 if (aco_process_config(&cfg_info, 0) == ACO_PROCESS_ERROR) {
599 /*! The agent is defined but an agent is not present. */
600 AGENT_STATE_LOGGED_OUT,
601 /*! Forced initial login wait to allow any local channel optimizations to happen. */
602 AGENT_STATE_PROBATION_WAIT,
603 /*! The agent is ready for a call. */
604 AGENT_STATE_READY_FOR_CALL,
605 /*! The agent has a call waiting to connect. */
606 AGENT_STATE_CALL_PRESENT,
607 /*! The agent needs to ack the call. */
608 AGENT_STATE_CALL_WAIT_ACK,
609 /*! The agent is connected with a call. */
611 /*! The agent is resting between calls. */
612 AGENT_STATE_CALL_WRAPUP,
613 /*! The agent is being kicked out. */
614 AGENT_STATE_LOGGING_OUT,
617 /*! Agent config option override flags. */
618 enum agent_override_flags {
619 AGENT_FLAG_ACK_CALL = (1 << 0),
620 AGENT_FLAG_DTMF_ACCEPT = (1 << 1),
621 AGENT_FLAG_AUTO_LOGOFF = (1 << 2),
622 AGENT_FLAG_WRAPUP_TIME = (1 << 3),
625 /*! \brief Structure representing an agent. */
627 AST_DECLARE_STRING_FIELDS(
628 /*! Identification of the agent. (agents container key) */
629 AST_STRING_FIELD(username);
630 /*! Login override DTMF string for an agent to accept a call. */
631 AST_STRING_FIELD(override_dtmf_accept);
633 /*! Connected line information to send when reentering the holding bridge. */
634 struct ast_party_connected_line waiting_colp;
635 /*! Flags show if settings were overridden by channel vars. */
637 /*! Login override number of seconds for agent to ack a call before being logged off. */
638 unsigned int override_auto_logoff;
639 /*! Login override time after a call in ms before the agent can get a new call. */
640 unsigned int override_wrapup_time;
641 /*! Login override if agent needs to ack a call to accept it. */
642 unsigned int override_ack_call:1;
644 /*! TRUE if the agent is requested to logoff when the current call ends. */
645 unsigned int deferred_logoff:1;
647 /*! Mark and sweep config update to determine if an agent is dead. */
648 unsigned int the_mark:1;
650 * \brief TRUE if the agent is no longer configured and is being destroyed.
652 * \note Agents cannot log in if they are dead.
656 /*! Agent control state variable. */
657 enum agent_state state;
658 /*! Custom device state of agent. */
659 enum ast_device_state devstate;
661 /*! When agent first logged in */
663 /*! When agent login probation started. */
664 time_t probation_start;
665 /*! When call started */
667 /*! When ack timer started */
668 struct timeval ack_time;
669 /*! When last disconnected */
670 struct timeval last_disconnect;
672 /*! Caller is waiting in this bridge for agent to join. (Holds ref) */
673 struct ast_bridge *caller_bridge;
674 /*! Agent is logged in with this channel. (Holds ref) (NULL if not logged in.) */
675 struct ast_channel *logged;
676 /*! Active config values from config file. (Holds ref) */
677 struct agent_cfg *cfg;
680 /*! Container of defined agents. */
681 static struct ao2_container *agents;
684 * \brief Lock the agent.
686 * \param agent Agent to lock
690 #define agent_lock(agent) _agent_lock(agent, __FILE__, __PRETTY_FUNCTION__, __LINE__, #agent)
691 static inline void _agent_lock(struct agent_pvt *agent, const char *file, const char *function, int line, const char *var)
693 __ao2_lock(agent, AO2_LOCK_REQ_MUTEX, file, function, line, var);
697 * \brief Unlock the agent.
699 * \param agent Agent to unlock
703 #define agent_unlock(agent) _agent_unlock(agent, __FILE__, __PRETTY_FUNCTION__, __LINE__, #agent)
704 static inline void _agent_unlock(struct agent_pvt *agent, const char *file, const char *function, int line, const char *var)
706 __ao2_unlock(agent, file, function, line, var);
711 * \brief Obtain the agent logged in channel lock if it exists.
714 * \param agent Pointer to the LOCKED agent_pvt.
716 * \note Assumes the agent lock is already obtained.
718 * \note Defined locking order is channel lock then agent lock.
722 static struct ast_channel *agent_lock_logged(struct agent_pvt *agent)
724 struct ast_channel *logged;
727 if (!agent->logged) { /* No owner. Nothing to do. */
731 /* If we don't ref the logged, it could be killed when we unlock the agent. */
732 logged = ast_channel_ref(agent->logged);
734 /* Locking logged requires us to lock channel, then agent. */
736 ast_channel_lock(logged);
739 /* Check if logged changed during agent unlock period */
740 if (logged != agent->logged) {
741 /* Channel changed. Unref and do another pass. */
742 ast_channel_unlock(logged);
743 ast_channel_unref(logged);
745 /* Channel stayed the same. Return it. */
753 * \brief Get the Agent:agent_id device state.
756 * \param agent_id Username of the agent.
759 * Search the agents container for the agent and return the
762 * \return Device state of the agent.
764 static enum ast_device_state agent_pvt_devstate_get(const char *agent_id)
766 RAII_VAR(struct agent_pvt *, agent, ao2_find(agents, agent_id, OBJ_KEY), ao2_cleanup);
769 return agent->devstate;
771 return AST_DEVICE_INVALID;
776 * \brief Request an agent device state be updated.
779 * \param agent_id Which agent needs the device state updated.
783 static void agent_devstate_changed(const char *agent_id)
785 ast_devstate_changed(AST_DEVICE_UNKNOWN, AST_DEVSTATE_CACHABLE, "Agent:%s", agent_id);
788 static void agent_pvt_destructor(void *vdoomed)
790 struct agent_pvt *doomed = vdoomed;
792 /* Make sure device state reflects agent destruction. */
793 if (!ast_strlen_zero(doomed->username)) {
794 ast_debug(1, "Agent %s: Destroyed.\n", doomed->username);
795 agent_devstate_changed(doomed->username);
798 ast_party_connected_line_free(&doomed->waiting_colp);
799 if (doomed->caller_bridge) {
800 ast_bridge_destroy(doomed->caller_bridge);
801 doomed->caller_bridge = NULL;
803 if (doomed->logged) {
804 doomed->logged = ast_channel_unref(doomed->logged);
806 ao2_cleanup(doomed->cfg);
808 ast_string_field_free_memory(doomed);
811 static struct agent_pvt *agent_pvt_new(struct agent_cfg *cfg)
813 struct agent_pvt *agent;
815 agent = ao2_alloc(sizeof(*agent), agent_pvt_destructor);
819 if (ast_string_field_init(agent, 32)) {
823 ast_string_field_set(agent, username, cfg->username);
824 ast_party_connected_line_init(&agent->waiting_colp);
827 agent->devstate = AST_DEVICE_UNAVAILABLE;
833 * \brief Agents ao2 container sort function.
836 * \param obj_left pointer to the (user-defined part) of an object.
837 * \param obj_right pointer to the (user-defined part) of an object.
838 * \param flags flags from ao2_callback()
839 * OBJ_POINTER - if set, 'obj_right', is an object.
840 * OBJ_KEY - if set, 'obj_right', is a search key item that is not an object.
841 * OBJ_PARTIAL_KEY - if set, 'obj_right', is a partial search key item that is not an object.
843 * \retval <0 if obj_left < obj_right
844 * \retval =0 if obj_left == obj_right
845 * \retval >0 if obj_left > obj_right
847 static int agent_pvt_sort_cmp(const void *obj_left, const void *obj_right, int flags)
849 const struct agent_pvt *agent_left = obj_left;
850 const struct agent_pvt *agent_right = obj_right;
851 const char *right_key = obj_right;
854 switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
857 right_key = agent_right->username;
860 cmp = strcmp(agent_left->username, right_key);
862 case OBJ_PARTIAL_KEY:
863 cmp = strncmp(agent_left->username, right_key, strlen(right_key));
871 * \brief ao2_find() callback function.
875 * found = ao2_find(agents, agent, OBJ_POINTER);
876 * found = ao2_find(agents, "agent-id", OBJ_KEY);
877 * found = ao2_find(agents, agent->logged, 0);
879 static int agent_pvt_cmp(void *obj, void *arg, int flags)
881 const struct agent_pvt *agent = obj;
884 switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
887 case OBJ_PARTIAL_KEY:
891 if (agent->logged == arg) {
901 static int agent_mark(void *obj, void *arg, int flags)
903 struct agent_pvt *agent = obj;
911 static void agents_mark(void)
913 ao2_callback(agents, 0, agent_mark, NULL);
916 static int agent_sweep(void *obj, void *arg, int flags)
918 struct agent_pvt *agent = obj;
922 if (agent->the_mark) {
925 /* Unlink dead agents immediately. */
932 static void agents_sweep(void)
934 struct ao2_iterator *iter;
935 struct agent_pvt *agent;
936 struct ast_channel *logged;
938 iter = ao2_callback(agents, OBJ_MULTIPLE | OBJ_UNLINK, agent_sweep, NULL);
942 for (; (agent = ao2_iterator_next(iter)); ao2_ref(agent, -1)) {
945 logged = ast_channel_ref(agent->logged);
954 "Forced logoff of agent %s(%s). Agent no longer configured.\n",
955 agent->username, ast_channel_name(logged));
956 ast_softhangup(logged, AST_SOFTHANGUP_EXPLICIT);
957 ast_channel_unref(logged);
959 ao2_iterator_destroy(iter);
962 static void agents_post_apply_config(void)
964 struct ao2_iterator iter;
965 struct agent_cfg *cfg;
966 RAII_VAR(struct agents_cfg *, cfgs, ao2_global_obj_ref(cfg_handle), ao2_cleanup);
968 ast_assert(cfgs != NULL);
971 iter = ao2_iterator_init(cfgs->agents, 0);
972 for (; (cfg = ao2_iterator_next(&iter)); ao2_ref(cfg, -1)) {
973 RAII_VAR(struct agent_pvt *, agent, ao2_find(agents, cfg->username, OBJ_KEY), ao2_cleanup);
978 if (!agent->logged) {
979 struct agent_cfg *cfg_old;
981 /* Replace the config of agents not logged in. */
982 cfg_old = agent->cfg;
985 ao2_cleanup(cfg_old);
990 agent = agent_pvt_new(cfg);
994 ao2_link(agents, agent);
995 ast_debug(1, "Agent %s: Created.\n", agent->username);
996 agent_devstate_changed(agent->username);
998 ao2_iterator_destroy(&iter);
1002 static int agent_logoff_request(const char *agent_id, int soft)
1004 struct ast_channel *logged;
1005 RAII_VAR(struct agent_pvt *, agent, ao2_find(agents, agent_id, OBJ_KEY), ao2_cleanup);
1012 logged = agent_lock_logged(agent);
1015 agent->deferred_logoff = 1;
1017 ast_softhangup(logged, AST_SOFTHANGUP_EXPLICIT);
1019 ast_channel_unlock(logged);
1020 ast_channel_unref(logged);
1022 agent_unlock(agent);
1026 /*! Agent holding bridge instance holder. */
1027 static AO2_GLOBAL_OBJ_STATIC(agent_holding);
1029 /*! Agent holding bridge deferred creation lock. */
1030 AST_MUTEX_DEFINE_STATIC(agent_holding_lock);
1034 * \brief Connect the agent with the waiting caller.
1037 * \param bridge_channel Agent channel connecting to the caller.
1038 * \param agent Which agent is connecting to the caller.
1040 * \note The agent is locked on entry and not locked on exit.
1044 static void agent_connect_caller(struct ast_bridge_channel *bridge_channel, struct agent_pvt *agent)
1046 struct ast_bridge *caller_bridge;
1047 int record_agent_calls;
1050 record_agent_calls = agent->cfg->record_agent_calls;
1051 caller_bridge = agent->caller_bridge;
1052 agent->caller_bridge = NULL;
1053 agent->state = AGENT_STATE_ON_CALL;
1054 time(&agent->call_start);
1055 agent_unlock(agent);
1057 if (!caller_bridge) {
1059 ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END);
1062 res = ast_bridge_move(caller_bridge, bridge_channel->bridge, bridge_channel->chan,
1066 ast_bridge_destroy(caller_bridge);
1067 ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END);
1070 ast_bridge_channel_write_control_data(bridge_channel, AST_CONTROL_ANSWER, NULL, 0);
1072 if (record_agent_calls) {
1073 struct ast_bridge_features_automixmonitor options = {
1074 .start_stop = AUTO_MONITOR_START,
1078 * The agent is in the new bridge so we can invoke the
1079 * mixmonitor hook to only start recording.
1081 ast_bridge_features_do(AST_BRIDGE_BUILTIN_AUTOMIXMON, caller_bridge,
1082 bridge_channel, &options);
1086 static int bridge_agent_hold_ack(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
1088 struct agent_pvt *agent = hook_pvt;
1091 switch (agent->state) {
1092 case AGENT_STATE_CALL_WAIT_ACK:
1093 /* Connect to caller now. */
1094 ast_debug(1, "Agent %s: Acked call.\n", agent->username);
1095 agent_connect_caller(bridge_channel, agent);/* Will unlock agent. */
1100 agent_unlock(agent);
1104 static int bridge_agent_hold_heartbeat(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
1106 struct agent_pvt *agent = hook_pvt;
1107 int probation_timedout = 0;
1108 int ack_timedout = 0;
1109 int wrapup_timedout = 0;
1110 int deferred_logoff;
1111 unsigned int wrapup_time;
1112 unsigned int auto_logoff;
1115 deferred_logoff = agent->deferred_logoff;
1116 if (deferred_logoff) {
1117 agent->state = AGENT_STATE_LOGGING_OUT;
1120 switch (agent->state) {
1121 case AGENT_STATE_PROBATION_WAIT:
1122 probation_timedout =
1123 LOGIN_WAIT_TIMEOUT_TIME <= (time(NULL) - agent->probation_start);
1124 if (probation_timedout) {
1125 /* Now ready for a caller. */
1126 agent->state = AGENT_STATE_READY_FOR_CALL;
1127 agent->devstate = AST_DEVICE_NOT_INUSE;
1130 case AGENT_STATE_CALL_WAIT_ACK:
1131 /* Check ack call time. */
1132 auto_logoff = agent->cfg->auto_logoff;
1133 if (ast_test_flag(agent, AGENT_FLAG_AUTO_LOGOFF)) {
1134 auto_logoff = agent->override_auto_logoff;
1137 auto_logoff *= 1000;
1138 ack_timedout = ast_tvdiff_ms(ast_tvnow(), agent->ack_time) > auto_logoff;
1140 agent->state = AGENT_STATE_LOGGING_OUT;
1144 case AGENT_STATE_CALL_WRAPUP:
1145 /* Check wrapup time. */
1146 wrapup_time = agent->cfg->wrapup_time;
1147 if (ast_test_flag(agent, AGENT_FLAG_WRAPUP_TIME)) {
1148 wrapup_time = agent->override_wrapup_time;
1150 wrapup_timedout = ast_tvdiff_ms(ast_tvnow(), agent->last_disconnect) > wrapup_time;
1151 if (wrapup_timedout) {
1152 agent->state = AGENT_STATE_READY_FOR_CALL;
1153 agent->devstate = AST_DEVICE_NOT_INUSE;
1159 agent_unlock(agent);
1161 if (deferred_logoff) {
1162 ast_debug(1, "Agent %s: Deferred logoff.\n", agent->username);
1163 ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END);
1164 } else if (probation_timedout) {
1165 ast_debug(1, "Agent %s: Login complete.\n", agent->username);
1166 agent_devstate_changed(agent->username);
1167 } else if (ack_timedout) {
1168 ast_debug(1, "Agent %s: Ack call timeout.\n", agent->username);
1169 ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END);
1170 } else if (wrapup_timedout) {
1171 ast_debug(1, "Agent %s: Wrapup timeout. Ready for new call.\n", agent->username);
1172 agent_devstate_changed(agent->username);
1178 static void agent_after_bridge_cb(struct ast_channel *chan, void *data);
1179 static void agent_after_bridge_cb_failed(enum ast_bridge_after_cb_reason reason, void *data);
1183 * \brief ast_bridge agent_hold push method.
1186 * \param self Bridge to operate upon.
1187 * \param bridge_channel Bridge channel to push.
1188 * \param swap Bridge channel to swap places with if not NULL.
1190 * \note On entry, self is already locked.
1192 * \retval 0 on success
1193 * \retval -1 on failure
1195 static int bridge_agent_hold_push(struct ast_bridge *self, struct ast_bridge_channel *bridge_channel, struct ast_bridge_channel *swap)
1198 unsigned int wrapup_time;
1199 char dtmf[AST_FEATURE_MAX_LEN];
1200 struct ast_channel *chan;
1201 const char *moh_class;
1202 RAII_VAR(struct agent_pvt *, agent, NULL, ao2_cleanup);
1204 chan = bridge_channel->chan;
1206 agent = ao2_find(agents, swap ? swap->chan : chan, 0);
1208 /* Could not find the agent. */
1212 /* Setup agent entertainment */
1214 moh_class = ast_strdupa(agent->cfg->moh);
1215 agent_unlock(agent);
1216 res |= ast_channel_add_bridge_role(chan, "holding_participant");
1217 res |= ast_channel_set_bridge_role_option(chan, "holding_participant", "idle_mode", "musiconhold");
1218 res |= ast_channel_set_bridge_role_option(chan, "holding_participant", "moh_class", moh_class);
1220 /* Add DTMF acknowledge hook. */
1223 if (ast_test_flag(agent, AGENT_FLAG_ACK_CALL)
1224 ? agent->override_ack_call : agent->cfg->ack_call) {
1225 const char *dtmf_accept;
1227 dtmf_accept = ast_test_flag(agent, AGENT_FLAG_DTMF_ACCEPT)
1228 ? agent->override_dtmf_accept : agent->cfg->dtmf_accept;
1229 ast_copy_string(dtmf, dtmf_accept, sizeof(dtmf));
1231 agent_unlock(agent);
1232 if (!ast_strlen_zero(dtmf)) {
1234 if (ast_bridge_dtmf_hook(bridge_channel->features, dtmf, bridge_agent_hold_ack,
1235 agent, __ao2_cleanup, AST_BRIDGE_HOOK_REMOVE_ON_PULL)) {
1241 /* Add heartbeat interval hook. */
1243 if (ast_bridge_interval_hook(bridge_channel->features, 1000,
1244 bridge_agent_hold_heartbeat, agent, __ao2_cleanup, AST_BRIDGE_HOOK_REMOVE_ON_PULL)) {
1249 res |= ast_bridge_base_v_table.push(self, bridge_channel, swap);
1251 ast_channel_remove_bridge_role(chan, "holding_participant");
1256 res = ast_bridge_set_after_callback(chan, agent_after_bridge_cb,
1257 agent_after_bridge_cb_failed, chan);
1259 ast_channel_remove_bridge_role(chan, "holding_participant");
1264 ast_channel_unref(agent->logged);
1265 agent->logged = ast_channel_ref(chan);
1266 agent_unlock(agent);
1269 * Kick the channel out so it can come back in fully controlled.
1270 * Otherwise, the after bridge callback will linger and the
1271 * agent will have some slightly different behavior in corner
1274 ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END);
1279 switch (agent->state) {
1280 case AGENT_STATE_LOGGED_OUT:
1282 * \todo XXX the login probation time should be only if it is needed.
1284 * Need to determine if there are any local channels that can
1285 * optimize and wait until they actually do before leaving the
1286 * AGENT_STATE_PROBATION_WAIT state. For now, the blind
1287 * timer of LOGIN_WAIT_TIMEOUT_TIME will do.
1290 * Start the login probation timer.
1292 * We cannot handle an agent local channel optimization when the
1293 * agent is on a call. The optimization may kick the agent
1294 * channel we know about out of the call without our being able
1295 * to switch to the replacement channel. Get any agent local
1296 * channel optimization out of the way while the agent is in the
1299 time(&agent->probation_start);
1300 agent->state = AGENT_STATE_PROBATION_WAIT;
1301 agent_unlock(agent);
1303 case AGENT_STATE_PROBATION_WAIT:
1304 /* Restart the probation timer. */
1305 time(&agent->probation_start);
1306 agent_unlock(agent);
1308 case AGENT_STATE_READY_FOR_CALL:
1310 * Likely someone manually kicked us out of the holding bridge
1311 * and we came right back in.
1313 agent_unlock(agent);
1316 /* Unexpected agent state. */
1319 case AGENT_STATE_CALL_PRESENT:
1320 case AGENT_STATE_CALL_WAIT_ACK:
1321 agent->state = AGENT_STATE_READY_FOR_CALL;
1322 agent->devstate = AST_DEVICE_NOT_INUSE;
1323 agent_unlock(agent);
1324 ast_debug(1, "Agent %s: Call abort recovery complete.\n", agent->username);
1325 agent_devstate_changed(agent->username);
1327 case AGENT_STATE_ON_CALL:
1328 case AGENT_STATE_CALL_WRAPUP:
1329 wrapup_time = agent->cfg->wrapup_time;
1330 if (ast_test_flag(agent, AGENT_FLAG_WRAPUP_TIME)) {
1331 wrapup_time = agent->override_wrapup_time;
1334 agent->state = AGENT_STATE_CALL_WRAPUP;
1336 agent->state = AGENT_STATE_READY_FOR_CALL;
1337 agent->devstate = AST_DEVICE_NOT_INUSE;
1339 agent_unlock(agent);
1341 /* No wrapup time. */
1342 ast_debug(1, "Agent %s: Ready for new call.\n", agent->username);
1343 agent_devstate_changed(agent->username);
1353 * \brief ast_bridge agent_hold pull method.
1355 * \param self Bridge to operate upon.
1356 * \param bridge_channel Bridge channel to pull.
1359 * Remove any channel hooks controlled by the bridge. Release
1360 * any resources held by bridge_channel->bridge_pvt and release
1361 * bridge_channel->bridge_pvt.
1363 * \note On entry, self is already locked.
1367 static void bridge_agent_hold_pull(struct ast_bridge *self, struct ast_bridge_channel *bridge_channel)
1369 ast_channel_remove_bridge_role(bridge_channel->chan, "holding_participant");
1370 ast_bridge_base_v_table.pull(self, bridge_channel);
1374 * \brief The bridge is being dissolved.
1376 * \param self Bridge to operate upon.
1379 * The bridge is being dissolved. Remove any external
1380 * references to the bridge so it can be destroyed.
1382 * \note On entry, self must NOT be locked.
1386 static void bridge_agent_hold_dissolving(struct ast_bridge *self)
1388 ao2_global_obj_replace_unref(agent_holding, NULL);
1389 ast_bridge_base_v_table.dissolving(self);
1392 static struct ast_bridge_methods bridge_agent_hold_v_table;
1394 static struct ast_bridge *bridge_agent_hold_new(void)
1396 struct ast_bridge *bridge;
1398 bridge = bridge_alloc(sizeof(struct ast_bridge), &bridge_agent_hold_v_table);
1399 bridge = bridge_base_init(bridge, AST_BRIDGE_CAPABILITY_HOLDING,
1400 AST_BRIDGE_FLAG_MERGE_INHIBIT_TO | AST_BRIDGE_FLAG_MERGE_INHIBIT_FROM
1401 | AST_BRIDGE_FLAG_SWAP_INHIBIT_FROM | AST_BRIDGE_FLAG_TRANSFER_PROHIBITED);
1402 bridge = bridge_register(bridge);
1406 static void bridging_init_agent_hold(void)
1408 /* Setup bridge agent_hold subclass v_table. */
1409 bridge_agent_hold_v_table = ast_bridge_base_v_table;
1410 bridge_agent_hold_v_table.name = "agent_hold";
1411 bridge_agent_hold_v_table.dissolving = bridge_agent_hold_dissolving;
1412 bridge_agent_hold_v_table.push = bridge_agent_hold_push;
1413 bridge_agent_hold_v_table.pull = bridge_agent_hold_pull;
1416 static int bridge_agent_hold_deferred_create(void)
1418 RAII_VAR(struct ast_bridge *, holding, ao2_global_obj_ref(agent_holding), ao2_cleanup);
1421 ast_mutex_lock(&agent_holding_lock);
1422 holding = ao2_global_obj_ref(agent_holding);
1424 holding = bridge_agent_hold_new();
1425 ao2_global_obj_replace_unref(agent_holding, holding);
1427 ast_mutex_unlock(&agent_holding_lock);
1429 ast_log(LOG_ERROR, "Could not create agent holding bridge.\n");
1436 static void send_agent_login(struct ast_channel *chan, const char *agent)
1438 RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
1440 ast_assert(agent != NULL);
1442 blob = ast_json_pack("{s: s}",
1448 ast_channel_publish_blob(chan, ast_channel_agent_login_type(), blob);
1451 static void send_agent_logoff(struct ast_channel *chan, const char *agent, long logintime)
1453 RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
1455 ast_assert(agent != NULL);
1457 blob = ast_json_pack("{s: s, s: i}",
1459 "logintime", logintime);
1464 ast_channel_publish_blob(chan, ast_channel_agent_logoff_type(), blob);
1469 * \brief Logout the agent.
1472 * \param agent Which agent logging out.
1474 * \note On entry agent is already locked. On exit it is no longer locked.
1478 static void agent_logout(struct agent_pvt *agent)
1480 struct ast_channel *logged;
1481 struct ast_bridge *caller_bridge;
1482 long time_logged_in;
1484 time_logged_in = time(NULL) - agent->login_start;
1485 logged = agent->logged;
1486 agent->logged = NULL;
1487 caller_bridge = agent->caller_bridge;
1488 agent->caller_bridge = NULL;
1489 agent->state = AGENT_STATE_LOGGED_OUT;
1490 agent->devstate = AST_DEVICE_UNAVAILABLE;
1491 ast_clear_flag(agent, AST_FLAGS_ALL);
1492 agent_unlock(agent);
1493 agent_devstate_changed(agent->username);
1495 if (caller_bridge) {
1496 ast_bridge_destroy(caller_bridge);
1499 send_agent_logoff(logged, agent->username, time_logged_in);
1500 ast_verb(2, "Agent '%s' logged out. Logged in for %ld seconds.\n",
1501 agent->username, time_logged_in);
1502 ast_channel_unref(logged);
1507 * \brief Agent driver loop.
1510 * \param agent Which agent.
1511 * \param logged The logged in channel.
1515 static void agent_run(struct agent_pvt *agent, struct ast_channel *logged)
1517 struct ast_bridge_features features;
1519 if (ast_bridge_features_init(&features)) {
1520 goto agent_run_cleanup;
1523 struct agents_cfg *cfgs;
1524 struct agent_cfg *cfg_new;
1525 struct agent_cfg *cfg_old;
1526 struct ast_bridge *holding;
1527 struct ast_bridge *caller_bridge;
1529 holding = ao2_global_obj_ref(agent_holding);
1531 ast_debug(1, "Agent %s: Someone destroyed the agent holding bridge.\n",
1537 * When the agent channel leaves the bridging system we usually
1538 * want to put the agent back into the holding bridge for the
1541 ast_bridge_join(holding, logged, NULL, &features, NULL, 1);
1542 if (logged != agent->logged) {
1543 /* This channel is no longer the logged in agent. */
1548 /* The agent is no longer configured. */
1552 /* Update the agent's config before rejoining the holding bridge. */
1553 cfgs = ao2_global_obj_ref(cfg_handle);
1555 /* There is no agent configuration. All agents were destroyed. */
1558 cfg_new = ao2_find(cfgs->agents, agent->username, OBJ_KEY);
1561 /* The agent is no longer configured. */
1565 cfg_old = agent->cfg;
1566 agent->cfg = cfg_new;
1568 agent->last_disconnect = ast_tvnow();
1570 /* Clear out any caller bridge before rejoining the holding bridge. */
1571 caller_bridge = agent->caller_bridge;
1572 agent->caller_bridge = NULL;
1573 agent_unlock(agent);
1574 ao2_ref(cfg_old, -1);
1575 if (caller_bridge) {
1576 ast_bridge_destroy(caller_bridge);
1579 if (agent->state == AGENT_STATE_LOGGING_OUT
1580 || agent->deferred_logoff
1581 || ast_check_hangup_locked(logged)) {
1582 /* The agent was requested to logout or hungup. */
1587 * It is safe to access agent->waiting_colp without a lock. It
1588 * is only setup on agent login and not changed.
1590 ast_channel_update_connected_line(logged, &agent->waiting_colp, NULL);
1592 ast_bridge_features_cleanup(&features);
1596 if (logged != agent->logged) {
1598 * We are no longer the agent channel because of local channel
1601 agent_unlock(agent);
1602 ast_debug(1, "Agent %s: Channel %s is no longer the agent.\n",
1603 agent->username, ast_channel_name(logged));
1606 agent_logout(agent);
1609 static void agent_after_bridge_cb(struct ast_channel *chan, void *data)
1611 struct agent_pvt *agent;
1613 agent = ao2_find(agents, chan, 0);
1618 ast_debug(1, "Agent %s: New agent channel %s.\n",
1619 agent->username, ast_channel_name(chan));
1620 agent_run(agent, chan);
1624 static void agent_after_bridge_cb_failed(enum ast_bridge_after_cb_reason reason, void *data)
1626 struct ast_channel *chan = data;
1627 struct agent_pvt *agent;
1629 agent = ao2_find(agents, chan, 0);
1633 ast_log(LOG_WARNING, "Agent %s: Forced logout. Lost control of %s because: %s\n",
1634 agent->username, ast_channel_name(chan),
1635 ast_bridge_after_cb_reason_string(reason));
1637 agent_logout(agent);
1643 * \brief Get the lock on the agent bridge_channel and return it.
1646 * \param agent Whose bridge_channel to get.
1648 * \retval bridge_channel on success (Reffed and locked).
1649 * \retval NULL on error.
1651 static struct ast_bridge_channel *agent_bridge_channel_get_lock(struct agent_pvt *agent)
1653 struct ast_channel *logged;
1654 struct ast_bridge_channel *bc;
1658 logged = agent->logged;
1660 agent_unlock(agent);
1663 ast_channel_ref(logged);
1664 agent_unlock(agent);
1666 ast_channel_lock(logged);
1667 bc = ast_channel_get_bridge_channel(logged);
1668 ast_channel_unlock(logged);
1669 ast_channel_unref(logged);
1671 if (agent->logged != logged) {
1677 ast_bridge_channel_lock(bc);
1678 if (bc->chan != logged || agent->logged != logged) {
1679 ast_bridge_channel_unlock(bc);
1687 static void caller_abort_agent(struct agent_pvt *agent)
1689 struct ast_bridge_channel *logged;
1691 logged = agent_bridge_channel_get_lock(agent);
1693 struct ast_bridge *caller_bridge;
1695 ast_debug(1, "Agent '%s' no longer logged in.\n", agent->username);
1698 caller_bridge = agent->caller_bridge;
1699 agent->caller_bridge = NULL;
1700 agent_unlock(agent);
1701 if (caller_bridge) {
1702 ast_bridge_destroy(caller_bridge);
1707 /* Kick the agent out of the holding bridge to reset it. */
1708 ast_bridge_channel_leave_bridge_nolock(logged, BRIDGE_CHANNEL_STATE_END);
1709 ast_bridge_channel_unlock(logged);
1712 static int caller_safety_timeout(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
1714 struct agent_pvt *agent = hook_pvt;
1716 if (agent->state == AGENT_STATE_CALL_PRESENT) {
1717 ast_verb(3, "Agent '%s' did not respond. Safety timeout.\n", agent->username);
1718 ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END);
1719 caller_abort_agent(agent);
1725 static void agent_alert(struct ast_bridge_channel *bridge_channel, const void *payload, size_t payload_size)
1727 const char *agent_id = payload;
1728 const char *playfile;
1729 RAII_VAR(struct agent_pvt *, agent, NULL, ao2_cleanup);
1731 agent = ao2_find(agents, agent_id, OBJ_KEY);
1733 ast_debug(1, "Agent '%s' does not exist. Where did it go?\n", agent_id);
1737 /* Alert the agent. */
1739 playfile = ast_strdupa(agent->cfg->beep_sound);
1740 agent_unlock(agent);
1741 ast_stream_and_wait(bridge_channel->chan, playfile, AST_DIGIT_NONE);
1744 switch (agent->state) {
1745 case AGENT_STATE_CALL_PRESENT:
1746 if (ast_test_flag(agent, AGENT_FLAG_ACK_CALL)
1747 ? agent->override_ack_call : agent->cfg->ack_call) {
1748 agent->state = AGENT_STATE_CALL_WAIT_ACK;
1749 agent->ack_time = ast_tvnow();
1753 /* Connect to caller now. */
1754 ast_debug(1, "Agent %s: Immediately connecting to call.\n", agent->username);
1755 agent_connect_caller(bridge_channel, agent);/* Will unlock agent. */
1760 agent_unlock(agent);
1763 static int send_alert_to_agent(struct ast_bridge_channel *bridge_channel, const char *agent_id)
1765 return ast_bridge_channel_queue_callback(bridge_channel, agent_alert, agent_id,
1766 strlen(agent_id) + 1);
1769 static int send_colp_to_agent(struct ast_bridge_channel *bridge_channel, struct ast_party_connected_line *connected)
1771 struct ast_set_party_connected_line update = {
1776 unsigned char data[1024]; /* This should be large enough */
1779 datalen = ast_connected_line_build_data(data, sizeof(data), connected, &update);
1780 if (datalen == (size_t) -1) {
1784 return ast_bridge_channel_queue_control_data(bridge_channel,
1785 AST_CONTROL_CONNECTED_LINE, data, datalen);
1789 * \brief Dialplan AgentRequest application to locate an agent to talk with.
1791 * \param chan Channel wanting to talk with an agent.
1792 * \param data Application parameters
1794 * \retval 0 To continue in dialplan.
1795 * \retval -1 To hangup.
1797 static int agent_request_exec(struct ast_channel *chan, const char *data)
1799 struct ast_bridge *caller_bridge;
1800 struct ast_bridge_channel *logged;
1803 struct ast_bridge_features caller_features;
1804 struct ast_party_connected_line connected;
1805 AST_DECLARE_APP_ARGS(args,
1806 AST_APP_ARG(agent_id);
1807 AST_APP_ARG(other); /* Any remaining unused arguments */
1810 RAII_VAR(struct agent_pvt *, agent, NULL, ao2_cleanup);
1812 if (bridge_agent_hold_deferred_create()) {
1816 parse = ast_strdupa(data);
1817 AST_STANDARD_APP_ARGS(args, parse);
1819 if (ast_strlen_zero(args.agent_id)) {
1820 ast_log(LOG_WARNING, "AgentRequest requires an AgentId\n");
1824 /* Find the agent. */
1825 agent = ao2_find(agents, args.agent_id, OBJ_KEY);
1827 ast_verb(3, "Agent '%s' does not exist.\n", args.agent_id);
1828 pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "INVALID");
1832 if (ast_bridge_features_init(&caller_features)) {
1836 /* Add safety timeout hook. */
1838 if (ast_bridge_interval_hook(&caller_features, CALLER_SAFETY_TIMEOUT_TIME,
1839 caller_safety_timeout, agent, __ao2_cleanup, AST_BRIDGE_HOOK_REMOVE_ON_PULL)) {
1841 ast_bridge_features_cleanup(&caller_features);
1845 caller_bridge = ast_bridge_basic_new();
1846 if (!caller_bridge) {
1847 ast_bridge_features_cleanup(&caller_features);
1851 /* Get COLP for agent. */
1852 ast_party_connected_line_init(&connected);
1853 ast_channel_lock(chan);
1854 ast_connected_line_copy_from_caller(&connected, ast_channel_caller(chan));
1855 ast_channel_unlock(chan);
1858 switch (agent->state) {
1859 case AGENT_STATE_LOGGED_OUT:
1860 case AGENT_STATE_LOGGING_OUT:
1861 agent_unlock(agent);
1862 ast_party_connected_line_free(&connected);
1863 ast_bridge_destroy(caller_bridge);
1864 ast_bridge_features_cleanup(&caller_features);
1865 ast_verb(3, "Agent '%s' not logged in.\n", agent->username);
1866 pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "NOT_LOGGED_IN");
1868 case AGENT_STATE_READY_FOR_CALL:
1869 ao2_ref(caller_bridge, +1);
1870 agent->caller_bridge = caller_bridge;
1871 agent->state = AGENT_STATE_CALL_PRESENT;
1872 agent->devstate = AST_DEVICE_INUSE;
1875 agent_unlock(agent);
1876 ast_party_connected_line_free(&connected);
1877 ast_bridge_destroy(caller_bridge);
1878 ast_bridge_features_cleanup(&caller_features);
1879 ast_verb(3, "Agent '%s' is busy.\n", agent->username);
1880 pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "BUSY");
1883 agent_unlock(agent);
1884 agent_devstate_changed(agent->username);
1886 logged = agent_bridge_channel_get_lock(agent);
1888 ast_party_connected_line_free(&connected);
1889 ast_bridge_destroy(caller_bridge);
1890 ast_bridge_features_cleanup(&caller_features);
1891 ast_verb(3, "Agent '%s' not logged in.\n", agent->username);
1892 pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "NOT_LOGGED_IN");
1893 caller_abort_agent(agent);
1897 send_colp_to_agent(logged, &connected);
1898 ast_party_connected_line_free(&connected);
1900 res = send_alert_to_agent(logged, agent->username);
1901 ast_bridge_channel_unlock(logged);
1902 ao2_ref(logged, -1);
1904 ast_bridge_destroy(caller_bridge);
1905 ast_bridge_features_cleanup(&caller_features);
1906 ast_verb(3, "Agent '%s': Failed to alert the agent.\n", agent->username);
1907 pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "ERROR");
1908 caller_abort_agent(agent);
1912 ast_indicate(chan, AST_CONTROL_RINGING);
1913 ast_bridge_join(caller_bridge, chan, NULL, &caller_features, NULL, 1);
1914 ast_bridge_features_cleanup(&caller_features);
1921 * \brief Get agent config values from the login channel.
1924 * \param agent What to setup channel config values on.
1925 * \param chan Channel logging in as an agent.
1929 static void agent_login_channel_config(struct agent_pvt *agent, struct ast_channel *chan)
1931 struct ast_flags opts = { 0 };
1932 struct ast_party_connected_line connected;
1933 unsigned int override_ack_call = 0;
1934 unsigned int override_auto_logoff = 0;
1935 unsigned int override_wrapup_time = 0;
1936 const char *override_dtmf_accept = NULL;
1939 ast_party_connected_line_init(&connected);
1941 /* Get config values from channel. */
1942 ast_channel_lock(chan);
1943 ast_party_connected_line_copy(&connected, ast_channel_connected(chan));
1945 var = pbx_builtin_getvar_helper(chan, "AGENTACKCALL");
1946 if (!ast_strlen_zero(var)) {
1947 override_ack_call = ast_true(var) ? 1 : 0;
1948 ast_set_flag(&opts, AGENT_FLAG_ACK_CALL);
1951 var = pbx_builtin_getvar_helper(chan, "AGENTACCEPTDTMF");
1952 if (!ast_strlen_zero(var)) {
1953 override_dtmf_accept = ast_strdupa(var);
1954 ast_set_flag(&opts, AGENT_FLAG_DTMF_ACCEPT);
1957 var = pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF");
1958 if (!ast_strlen_zero(var)) {
1959 if (sscanf(var, "%u", &override_auto_logoff) == 1) {
1960 ast_set_flag(&opts, AGENT_FLAG_AUTO_LOGOFF);
1964 var = pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME");
1965 if (!ast_strlen_zero(var)) {
1966 if (sscanf(var, "%u", &override_wrapup_time) == 1) {
1967 ast_set_flag(&opts, AGENT_FLAG_WRAPUP_TIME);
1970 ast_channel_unlock(chan);
1972 /* Set config values on agent. */
1974 ast_party_connected_line_free(&agent->waiting_colp);
1975 agent->waiting_colp = connected;
1977 ast_string_field_set(agent, override_dtmf_accept, override_dtmf_accept);
1978 ast_copy_flags(agent, &opts, AST_FLAGS_ALL);
1979 agent->override_auto_logoff = override_auto_logoff;
1980 agent->override_wrapup_time = override_wrapup_time;
1981 agent->override_ack_call = override_ack_call;
1982 agent_unlock(agent);
1985 enum AGENT_LOGIN_OPT_FLAGS {
1986 OPT_SILENT = (1 << 0),
1988 AST_APP_OPTIONS(agent_login_opts, BEGIN_OPTIONS
1989 AST_APP_OPTION('s', OPT_SILENT),
1993 * \brief Dialplan AgentLogin application to log in an agent.
1995 * \param chan Channel attempting to login as an agent.
1996 * \param data Application parameters
1998 * \retval 0 To continue in dialplan.
1999 * \retval -1 To hangup.
2001 static int agent_login_exec(struct ast_channel *chan, const char *data)
2004 struct ast_flags opts;
2005 AST_DECLARE_APP_ARGS(args,
2006 AST_APP_ARG(agent_id);
2007 AST_APP_ARG(options);
2008 AST_APP_ARG(other); /* Any remaining unused arguments */
2011 RAII_VAR(struct agent_pvt *, agent, NULL, ao2_cleanup);
2013 if (bridge_agent_hold_deferred_create()) {
2017 if (ast_channel_state(chan) != AST_STATE_UP && ast_answer(chan)) {
2021 parse = ast_strdupa(data);
2022 AST_STANDARD_APP_ARGS(args, parse);
2024 if (ast_strlen_zero(args.agent_id)) {
2025 ast_log(LOG_WARNING, "AgentLogin requires an AgentId\n");
2029 if (ast_app_parse_options(agent_login_opts, &opts, NULL, args.options)) {
2030 /* General invalid option syntax. */
2034 /* Find the agent. */
2035 agent = ao2_find(agents, args.agent_id, OBJ_KEY);
2037 ast_verb(3, "Agent '%s' does not exist.\n", args.agent_id);
2038 pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "INVALID");
2042 /* Has someone already logged in as this agent already? */
2044 if (agent->logged) {
2045 agent_unlock(agent);
2046 ast_verb(3, "Agent '%s' already logged in.\n", agent->username);
2047 pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "ALREADY_LOGGED_IN");
2050 agent->logged = ast_channel_ref(chan);
2051 agent->last_disconnect = ast_tvnow();
2052 time(&agent->login_start);
2053 agent_unlock(agent);
2055 agent_login_channel_config(agent, chan);
2057 if (!ast_test_flag(&opts, OPT_SILENT)
2058 && !ast_streamfile(chan, "agent-loginok", ast_channel_language(chan))) {
2059 ast_waitstream(chan, "");
2062 ast_verb(2, "Agent '%s' logged in (format %s/%s)\n", agent->username,
2063 ast_getformatname(ast_channel_readformat(chan)),
2064 ast_getformatname(ast_channel_writeformat(chan)));
2065 send_agent_login(chan, agent->username);
2067 agent_run(agent, chan);
2071 static int agent_function_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
2074 struct agent_pvt *agent;
2075 struct ast_channel *logged;
2076 AST_DECLARE_APP_ARGS(args,
2077 AST_APP_ARG(agentid);
2083 parse = ast_strdupa(data ?: "");
2084 AST_NONSTANDARD_APP_ARGS(args, parse, ':');
2086 if (ast_strlen_zero(args.agentid)) {
2087 ast_log(LOG_WARNING, "The AGENT function requires an argument - agentid!\n");
2091 args.item = "status";
2094 agent = ao2_find(agents, args.agentid, OBJ_KEY);
2096 ast_log(LOG_WARNING, "Agent '%s' not found!\n", args.agentid);
2101 if (!strcasecmp(args.item, "status")) {
2104 if (agent->logged) {
2105 status = "LOGGEDIN";
2107 status = "LOGGEDOUT";
2109 ast_copy_string(buf, status, len);
2110 } else if (!strcasecmp(args.item, "name")) {
2111 ast_copy_string(buf, agent->cfg->full_name, len);
2112 } else if (!strcasecmp(args.item, "mohclass")) {
2113 ast_copy_string(buf, agent->cfg->moh, len);
2114 } else if (!strcasecmp(args.item, "channel")) {
2115 logged = agent_lock_logged(agent);
2119 ast_copy_string(buf, ast_channel_name(logged), len);
2120 ast_channel_unlock(logged);
2121 ast_channel_unref(logged);
2123 pos = strrchr(buf, '-');
2128 } else if (!strcasecmp(args.item, "fullchannel")) {
2129 logged = agent_lock_logged(agent);
2131 ast_copy_string(buf, ast_channel_name(logged), len);
2132 ast_channel_unlock(logged);
2133 ast_channel_unref(logged);
2136 agent_unlock(agent);
2142 static struct ast_custom_function agent_function = {
2144 .read = agent_function_read,
2147 struct agent_complete {
2148 /*! Nth match to return. */
2150 /*! Which match currently on. */
2154 static int complete_agent_search(void *obj, void *arg, void *data, int flags)
2156 struct agent_complete *search = data;
2158 if (++search->which > search->state) {
2164 static char *complete_agent(const char *word, int state)
2167 struct agent_pvt *agent;
2168 struct agent_complete search = {
2172 agent = ao2_callback_data(agents, ast_strlen_zero(word) ? 0 : OBJ_PARTIAL_KEY,
2173 complete_agent_search, (char *) word, &search);
2177 ret = ast_strdup(agent->username);
2182 static int complete_agent_logoff_search(void *obj, void *arg, void *data, int flags)
2184 struct agent_pvt *agent = obj;
2185 struct agent_complete *search = data;
2187 if (!agent->logged) {
2190 if (++search->which > search->state) {
2196 static char *complete_agent_logoff(const char *word, int state)
2199 struct agent_pvt *agent;
2200 struct agent_complete search = {
2204 agent = ao2_callback_data(agents, ast_strlen_zero(word) ? 0 : OBJ_PARTIAL_KEY,
2205 complete_agent_logoff_search, (char *) word, &search);
2209 ret = ast_strdup(agent->username);
2214 static void agent_show_requested(struct ast_cli_args *a, int online_only)
2216 #define FORMAT_HDR "%-8s %-20s %-11s %-30s %s\n"
2217 #define FORMAT_ROW "%-8s %-20s %-11s %-30s %s\n"
2219 struct ao2_iterator iter;
2220 struct agent_pvt *agent;
2221 struct ast_str *out = ast_str_alloca(512);
2222 unsigned int agents_total = 0;
2223 unsigned int agents_logged_in = 0;
2224 unsigned int agents_talking = 0;
2226 ast_cli(a->fd, FORMAT_HDR, "Agent-ID", "Name", "State", "Channel", "Talking with");
2227 iter = ao2_iterator_init(agents, 0);
2228 for (; (agent = ao2_iterator_next(&iter)); ao2_ref(agent, -1)) {
2229 struct ast_channel *logged;
2234 logged = agent_lock_logged(agent);
2236 const char *talking_with;
2240 talking_with = pbx_builtin_getvar_helper(logged, "BRIDGEPEER");
2241 if (!ast_strlen_zero(talking_with)) {
2246 ast_str_set(&out, 0, FORMAT_ROW, agent->username, agent->cfg->full_name,
2247 ast_devstate_str(agent->devstate), ast_channel_name(logged), talking_with);
2248 ast_channel_unlock(logged);
2249 ast_channel_unref(logged);
2251 ast_str_set(&out, 0, FORMAT_ROW, agent->username, agent->cfg->full_name,
2252 ast_devstate_str(agent->devstate), "", "");
2254 agent_unlock(agent);
2256 if (!online_only || logged) {
2257 ast_cli(a->fd, "%s", ast_str_buffer(out));
2260 ao2_iterator_destroy(&iter);
2262 ast_cli(a->fd, "\nDefined agents: %u, Logged in: %u, Talking: %u\n",
2263 agents_total, agents_logged_in, agents_talking);
2269 static char *agent_handle_show_online(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2273 e->command = "agent show online";
2275 "Usage: agent show online\n"
2276 " Provides summary information for logged in agents.\n";
2283 return CLI_SHOWUSAGE;
2286 agent_show_requested(a, 1);
2291 static char *agent_handle_show_all(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2295 e->command = "agent show all";
2297 "Usage: agent show all\n"
2298 " Provides summary information for all agents.\n";
2305 return CLI_SHOWUSAGE;
2308 agent_show_requested(a, 0);
2313 static char *agent_handle_show_specific(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2315 struct agent_pvt *agent;
2316 struct ast_channel *logged;
2317 struct ast_str *out = ast_str_alloca(4096);
2321 e->command = "agent show";
2323 "Usage: agent show <agent-id>\n"
2324 " Show information about the <agent-id> agent\n";
2328 return complete_agent(a->word, a->n);
2334 return CLI_SHOWUSAGE;
2337 agent = ao2_find(agents, a->argv[2], OBJ_KEY);
2339 ast_cli(a->fd, "Agent '%s' not found\n", a->argv[2]);
2344 logged = agent_lock_logged(agent);
2345 ast_str_set(&out, 0, "Id: %s\n", agent->username);
2346 ast_str_append(&out, 0, "Name: %s\n", agent->cfg->full_name);
2347 ast_str_append(&out, 0, "Beep: %s\n", agent->cfg->beep_sound);
2348 ast_str_append(&out, 0, "MOH: %s\n", agent->cfg->moh);
2349 ast_str_append(&out, 0, "RecordCalls: %s\n", AST_CLI_YESNO(agent->cfg->record_agent_calls));
2350 ast_str_append(&out, 0, "State: %s\n", ast_devstate_str(agent->devstate));
2352 const char *talking_with;
2354 ast_str_append(&out, 0, "LoggedInChannel: %s\n", ast_channel_name(logged));
2355 ast_str_append(&out, 0, "LoggedInTime: %ld\n", (long) agent->login_start);
2356 talking_with = pbx_builtin_getvar_helper(logged, "BRIDGEPEER");
2357 if (!ast_strlen_zero(talking_with)) {
2358 ast_str_append(&out, 0, "TalkingWith: %s\n", talking_with);
2359 ast_str_append(&out, 0, "CallStarted: %ld\n", (long) agent->call_start);
2361 ast_channel_unlock(logged);
2362 ast_channel_unref(logged);
2364 agent_unlock(agent);
2367 ast_cli(a->fd, "%s", ast_str_buffer(out));
2372 static char *agent_handle_logoff_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2376 e->command = "agent logoff";
2378 "Usage: agent logoff <agent-id> [soft]\n"
2379 " Sets an agent as no longer logged in.\n"
2380 " If 'soft' is specified, do not hangup existing calls.\n";
2384 return complete_agent_logoff(a->word, a->n);
2385 } else if (a->pos == 3 && a->n == 0
2386 && (ast_strlen_zero(a->word)
2387 || !strncasecmp("soft", a->word, strlen(a->word)))) {
2388 return ast_strdup("soft");
2393 if (a->argc < 3 || 4 < a->argc) {
2394 return CLI_SHOWUSAGE;
2396 if (a->argc == 4 && strcasecmp(a->argv[3], "soft")) {
2397 return CLI_SHOWUSAGE;
2400 if (!agent_logoff_request(a->argv[2], a->argc == 4)) {
2401 ast_cli(a->fd, "Logging out %s\n", a->argv[2]);
2407 static struct ast_cli_entry cli_agents[] = {
2408 AST_CLI_DEFINE(agent_handle_show_online, "Show status of online agents"),
2409 AST_CLI_DEFINE(agent_handle_show_all, "Show status of all agents"),
2410 AST_CLI_DEFINE(agent_handle_show_specific, "Show information about an agent"),
2411 AST_CLI_DEFINE(agent_handle_logoff_cmd, "Sets an agent offline"),
2414 static int action_agents(struct mansession *s, const struct message *m)
2416 const char *id = astman_get_header(m, "ActionID");
2417 char id_text[AST_MAX_BUF];
2418 struct ao2_iterator iter;
2419 struct agent_pvt *agent;
2420 struct ast_str *out = ast_str_alloca(4096);
2422 if (!ast_strlen_zero(id)) {
2423 snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", id);
2427 astman_send_ack(s, m, "Agents will follow");
2429 iter = ao2_iterator_init(agents, 0);
2430 for (; (agent = ao2_iterator_next(&iter)); ao2_ref(agent, -1)) {
2431 struct ast_channel *logged;
2434 logged = agent_lock_logged(agent);
2438 * AGENT_LOGGEDOFF - Agent isn't logged in
2439 * AGENT_IDLE - Agent is logged in, and waiting for call
2440 * AGENT_ONCALL - Agent is logged in, and on a call
2441 * AGENT_UNKNOWN - Don't know anything about agent. Shouldn't ever get this.
2443 ast_str_set(&out, 0, "Agent: %s\r\n", agent->username);
2444 ast_str_append(&out, 0, "Name: %s\r\n", agent->cfg->full_name);
2447 const char *talking_to_chan;
2448 struct ast_str *logged_headers;
2449 RAII_VAR(struct ast_channel_snapshot *, logged_snapshot, ast_channel_snapshot_create(logged), ao2_cleanup);
2451 if (!logged_snapshot
2452 || !(logged_headers =
2453 ast_manager_build_channel_state_string(logged_snapshot))) {
2454 ast_channel_unlock(logged);
2455 ast_channel_unref(logged);
2456 agent_unlock(agent);
2460 talking_to_chan = pbx_builtin_getvar_helper(logged, "BRIDGEPEER");
2461 if (!ast_strlen_zero(talking_to_chan)) {
2462 ast_str_append(&out, 0, "Status: %s\r\n", "AGENT_ONCALL");
2463 ast_str_append(&out, 0, "TalkingToChan: %s\r\n", talking_to_chan);
2464 ast_str_append(&out, 0, "CallStarted: %ld\n", (long) agent->call_start);
2466 ast_str_append(&out, 0, "Status: %s\r\n", "AGENT_IDLE");
2468 ast_str_append(&out, 0, "LoggedInTime: %ld\r\n", (long) agent->login_start);
2469 ast_str_append(&out, 0, "%s", ast_str_buffer(logged_headers));
2470 ast_channel_unlock(logged);
2471 ast_channel_unref(logged);
2472 ast_free(logged_headers);
2474 ast_str_append(&out, 0, "Status: %s\r\n", "AGENT_LOGGEDOFF");
2477 agent_unlock(agent);
2479 astman_append(s, "Event: Agents\r\n"
2481 ast_str_buffer(out), id_text);
2483 ao2_iterator_destroy(&iter);
2485 astman_append(s, "Event: AgentsComplete\r\n"
2491 static int action_agent_logoff(struct mansession *s, const struct message *m)
2493 const char *agent = astman_get_header(m, "Agent");
2494 const char *soft_s = astman_get_header(m, "Soft"); /* "true" is don't hangup */
2496 if (ast_strlen_zero(agent)) {
2497 astman_send_error(s, m, "No agent specified");
2501 if (!agent_logoff_request(agent, ast_true(soft_s))) {
2502 astman_send_ack(s, m, "Agent logged out");
2504 astman_send_error(s, m, "No such agent");
2510 static int unload_module(void)
2512 struct ast_bridge *holding;
2514 /* Unregister dialplan applications */
2515 ast_unregister_application(app_agent_login);
2516 ast_unregister_application(app_agent_request);
2518 /* Unregister dialplan functions */
2519 ast_custom_function_unregister(&agent_function);
2521 /* Unregister manager command */
2522 ast_manager_unregister("Agents");
2523 ast_manager_unregister("AgentLogoff");
2525 /* Unregister CLI commands */
2526 ast_cli_unregister_multiple(cli_agents, ARRAY_LEN(cli_agents));
2528 ast_devstate_prov_del("Agent");
2530 /* Destroy agent holding bridge. */
2531 holding = ao2_global_obj_replace(agent_holding, NULL);
2533 ast_bridge_destroy(holding);
2537 ao2_ref(agents, -1);
2542 static int load_module(void)
2546 agents = ao2_container_alloc_rbtree(AO2_ALLOC_OPT_LOCK_MUTEX,
2547 AO2_CONTAINER_ALLOC_OPT_DUPS_REPLACE, agent_pvt_sort_cmp, agent_pvt_cmp);
2549 return AST_MODULE_LOAD_FAILURE;
2551 if (load_config()) {
2552 ast_log(LOG_ERROR, "Unable to load config. Not loading module.\n");
2553 ao2_ref(agents, -1);
2555 return AST_MODULE_LOAD_DECLINE;
2558 /* Init agent holding bridge v_table. */
2559 bridging_init_agent_hold();
2561 /* Setup to provide Agent:agent-id device state. */
2562 res |= ast_devstate_prov_add("Agent", agent_pvt_devstate_get);
2565 res |= ast_cli_register_multiple(cli_agents, ARRAY_LEN(cli_agents));
2567 /* Manager commands */
2568 res |= ast_manager_register_xml("Agents", EVENT_FLAG_AGENT, action_agents);
2569 res |= ast_manager_register_xml("AgentLogoff", EVENT_FLAG_AGENT, action_agent_logoff);
2571 /* Dialplan Functions */
2572 res |= ast_custom_function_register(&agent_function);
2574 /* Dialplan applications */
2575 res |= ast_register_application_xml(app_agent_login, agent_login_exec);
2576 res |= ast_register_application_xml(app_agent_request, agent_request_exec);
2580 return AST_MODULE_LOAD_FAILURE;
2582 return AST_MODULE_LOAD_SUCCESS;
2585 static int reload(void)
2587 if (aco_process_config(&cfg_info, 1) == ACO_PROCESS_ERROR) {
2588 /* Just keep the config we already have in place. */
2594 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Call center agent pool applications",
2595 .load = load_module,
2596 .unload = unload_module,
2598 .load_pri = AST_MODPRI_DEVSTATE_PROVIDER,