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_basic.h"
45 #include "asterisk/config_options.h"
46 #include "asterisk/features_config.h"
47 #include "asterisk/astobj2.h"
48 #include "asterisk/stringfields.h"
49 #include "asterisk/stasis_channels.h"
52 <application name="AgentLogin" language="en_US">
57 <parameter name="AgentId" required="true" />
58 <parameter name="options">
61 <para>silent login - do not announce the login ok segment after
62 agent logged on.</para>
68 <para>Login an agent to the system. Any agent authentication is assumed to
69 already be done by dialplan. While logged in, the agent can receive calls
70 and will hear a configurable <literal>beep</literal> sound when a new call
71 comes in for the agent. Login failures will continue in the dialplan
72 with <variable>AGENT_STATUS</variable> set.</para>
73 <para>Before logging in, you can setup on the real agent channel the
74 CHANNEL(dtmf-features) an agent will have when talking to a caller
75 and you can setup on the channel running this application the
76 CONNECTEDLINE() information the agent will see while waiting for a
78 <para><variable>AGENT_STATUS</variable> enumeration values:</para>
80 <enum name = "INVALID"><para>The specified agent is invalid.</para></enum>
81 <enum name = "ALREADY_LOGGED_IN"><para>The agent is already logged in.</para></enum>
83 <note><para>The Agents:<replaceable>AgentId</replaceable> device state is
84 available to monitor the status of the agent.</para></note>
87 <ref type="application">Authenticate</ref>
88 <ref type="application">Queue</ref>
89 <ref type="application">AddQueueMember</ref>
90 <ref type="application">RemoveQueueMember</ref>
91 <ref type="application">PauseQueueMember</ref>
92 <ref type="application">UnpauseQueueMember</ref>
93 <ref type="function">AGENT</ref>
94 <ref type="function">CHANNEL(dtmf-features)</ref>
95 <ref type="function">CONNECTEDLINE()</ref>
96 <ref type="filename">agents.conf</ref>
97 <ref type="filename">queues.conf</ref>
100 <application name="AgentRequest" language="en_US">
102 Request an agent to connect with the channel.
105 <parameter name="AgentId" required="true" />
108 <para>Request an agent to connect with the channel. Failure to find and
109 alert an agent will continue in the dialplan with <variable>AGENT_STATUS</variable> set.</para>
110 <para><variable>AGENT_STATUS</variable> enumeration values:</para>
112 <enum name = "INVALID"><para>The specified agent is invalid.</para></enum>
113 <enum name = "NOT_LOGGED_IN"><para>The agent is not available.</para></enum>
114 <enum name = "BUSY"><para>The agent is on another call.</para></enum>
115 <enum name = "ERROR"><para>Alerting the agent failed.</para></enum>
119 <ref type="application">AgentLogin</ref>
122 <function name="AGENT" language="en_US">
124 Gets information about an Agent
127 <parameter name="AgentId" required="true" />
128 <parameter name="item">
129 <para>The valid items to retrieve are:</para>
132 <para>(default) The status of the agent (LOGGEDIN | LOGGEDOUT)</para>
134 <enum name="password">
135 <para>Deprecated. The dialplan handles any agent authentication.</para>
138 <para>The name of the agent</para>
140 <enum name="mohclass">
141 <para>MusicOnHold class</para>
143 <enum name="channel">
144 <para>The name of the active channel for the Agent (AgentLogin)</para>
146 <enum name="fullchannel">
147 <para>The untruncated name of the active channel for the Agent (AgentLogin)</para>
152 <description></description>
154 <manager name="Agents" language="en_US">
156 Lists agents and their status.
159 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
162 <para>Will list info about all defined agents.</para>
165 <manager name="AgentLogoff" language="en_US">
167 Sets an agent as no longer logged in.
170 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
171 <parameter name="Agent" required="true">
172 <para>Agent ID of the agent to log off.</para>
174 <parameter name="Soft">
175 <para>Set to <literal>true</literal> to not hangup existing calls.</para>
179 <para>Sets an agent as no longer logged in.</para>
182 <configInfo name="app_agent_pool" language="en_US">
183 <synopsis>Agent pool applications</synopsis>
185 <note><para>Option changes take effect on agent login or after an agent
186 disconnects from a call.</para></note>
188 <configFile name="agents.conf">
189 <configObject name="global">
190 <synopsis>Unused, but reserved.</synopsis>
192 <configObject name="agent-id">
193 <synopsis>Configure an agent for the pool.</synopsis>
195 <xi:include xpointer="xpointer(/docs/configInfo[@name='app_agent_pool']/description/note)" />
197 <configOption name="ackcall">
198 <synopsis>Enable to require the agent to acknowledge a call.</synopsis>
200 <para>Enable to require the agent to give a DTMF acknowledgement
201 when the agent receives a call.</para>
202 <note><para>The option is overridden by <variable>AGENTACKCALL</variable> on agent login.</para></note>
203 <xi:include xpointer="xpointer(/docs/configInfo[@name='app_agent_pool']/description/note)" />
206 <configOption name="acceptdtmf">
207 <synopsis>DTMF key sequence the agent uses to acknowledge a call.</synopsis>
209 <note><para>The option is overridden by <variable>AGENTACCEPTDTMF</variable> on agent login.</para></note>
210 <xi:include xpointer="xpointer(/docs/configInfo[@name='app_agent_pool']/description/note)" />
213 <configOption name="autologoff">
214 <synopsis>Time the agent has to acknowledge a call before being logged off.</synopsis>
216 <para>Set how many seconds a call for the agent has to wait for the
217 agent to acknowledge the call before the agent is automatically
218 logged off. If set to zero then the call will wait forever for
219 the agent to acknowledge.</para>
220 <note><para>The option is overridden by <variable>AGENTAUTOLOGOFF</variable> on agent login.</para></note>
221 <xi:include xpointer="xpointer(/docs/configInfo[@name='app_agent_pool']/description/note)" />
224 <configOption name="wrapuptime">
225 <synopsis>Minimum time the agent has between calls.</synopsis>
227 <para>Set the minimum amount of time in milliseconds after
228 disconnecting a call before the agent can receive a new call.</para>
229 <note><para>The option is overridden by <variable>AGENTWRAPUPTIME</variable> on agent login.</para></note>
230 <xi:include xpointer="xpointer(/docs/configInfo[@name='app_agent_pool']/description/note)" />
233 <configOption name="musiconhold">
234 <synopsis>Music on hold class the agent listens to between calls.</synopsis>
236 <xi:include xpointer="xpointer(/docs/configInfo[@name='app_agent_pool']/description/note)" />
239 <configOption name="recordagentcalls">
240 <synopsis>Enable to automatically record calls the agent takes.</synopsis>
242 <para>Enable recording calls the agent takes automatically by
243 invoking the automixmon DTMF feature when the agent connects
244 to a caller. See <filename>features.conf.sample</filename> for information about
245 the automixmon feature.</para>
246 <xi:include xpointer="xpointer(/docs/configInfo[@name='app_agent_pool']/description/note)" />
249 <configOption name="custom_beep">
250 <synopsis>Sound file played to alert the agent when a call is present.</synopsis>
252 <xi:include xpointer="xpointer(/docs/configInfo[@name='app_agent_pool']/description/note)" />
255 <configOption name="fullname">
256 <synopsis>A friendly name for the agent used in log messages.</synopsis>
258 <xi:include xpointer="xpointer(/docs/configInfo[@name='app_agent_pool']/description/note)" />
266 /* ------------------------------------------------------------------- */
268 #define AST_MAX_BUF 256
270 /*! Maximum wait time (in ms) for the custom_beep file to play announcing the caller. */
271 #define CALLER_SAFETY_TIMEOUT_TIME (2 * 60 * 1000)
273 /*! Number of seconds to wait for local channel optimizations to complete. */
274 #define LOGIN_WAIT_TIMEOUT_TIME 5
276 static const char app_agent_login[] = "AgentLogin";
277 static const char app_agent_request[] = "AgentRequest";
279 /*! Agent config parameters. */
281 AST_DECLARE_STRING_FIELDS(
282 /*! Identification of the agent. (agents config container key) */
283 AST_STRING_FIELD(username);
284 /*! Name of agent for logging and querying purposes */
285 AST_STRING_FIELD(full_name);
288 * \brief DTMF string for an agent to accept a call.
290 * \note The channel variable AGENTACCEPTDTMF overrides on login.
292 AST_STRING_FIELD(dtmf_accept);
293 /*! Beep sound file to use. Alert the agent a call is waiting. */
294 AST_STRING_FIELD(beep_sound);
295 /*! MOH class to use while agent waiting for call. */
296 AST_STRING_FIELD(moh);
299 * \brief Number of seconds for agent to ack a call before being logged off.
301 * \note The channel variable AGENTAUTOLOGOFF overrides on login.
302 * \note If zero then timer is disabled.
304 unsigned int auto_logoff;
306 * \brief Time after a call in ms before the agent can get a new call.
308 * \note The channel variable AGENTWRAPUPTIME overrides on login.
310 unsigned int wrapup_time;
312 * \brief TRUE if agent needs to ack a call to accept it.
314 * \note The channel variable AGENTACKCALL overrides on login.
317 /*! TRUE if agent calls are automatically recorded. */
318 int record_agent_calls;
323 * \brief Agent config ao2 container sort function.
326 * \param obj_left pointer to the (user-defined part) of an object.
327 * \param obj_right pointer to the (user-defined part) of an object.
328 * \param flags flags from ao2_callback()
329 * OBJ_POINTER - if set, 'obj_right', is an object.
330 * OBJ_KEY - if set, 'obj_right', is a search key item that is not an object.
331 * OBJ_PARTIAL_KEY - if set, 'obj_right', is a partial search key item that is not an object.
333 * \retval <0 if obj_left < obj_right
334 * \retval =0 if obj_left == obj_right
335 * \retval >0 if obj_left > obj_right
337 static int agent_cfg_sort_cmp(const void *obj_left, const void *obj_right, int flags)
339 const struct agent_cfg *cfg_left = obj_left;
340 const struct agent_cfg *cfg_right = obj_right;
341 const char *right_key = obj_right;
344 switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
347 right_key = cfg_right->username;
350 cmp = strcmp(cfg_left->username, right_key);
352 case OBJ_PARTIAL_KEY:
353 cmp = strncmp(cfg_left->username, right_key, strlen(right_key));
359 static void agent_cfg_destructor(void *vdoomed)
361 struct agent_cfg *doomed = vdoomed;
363 ast_string_field_free_memory(doomed);
366 static void *agent_cfg_alloc(const char *name)
368 struct agent_cfg *cfg;
370 cfg = ao2_alloc_options(sizeof(*cfg), agent_cfg_destructor,
371 AO2_ALLOC_OPT_LOCK_NOLOCK);
372 if (!cfg || ast_string_field_init(cfg, 64)) {
375 ast_string_field_set(cfg, username, name);
379 static void *agent_cfg_find(struct ao2_container *agents, const char *username)
381 return ao2_find(agents, username, OBJ_KEY);
384 /*! Agents configuration */
386 /*! Master configured agents container. */
387 struct ao2_container *agents;
390 static struct aco_type agent_type = {
393 .category_match = ACO_BLACKLIST,
394 .category = "^(general|agents)$",
395 .item_alloc = agent_cfg_alloc,
396 .item_find = agent_cfg_find,
397 .item_offset = offsetof(struct agents_cfg, agents),
400 static struct aco_type *agent_types[] = ACO_TYPES(&agent_type);
402 /* The general category is reserved, but unused */
403 static struct aco_type general_type = {
406 .category_match = ACO_WHITELIST,
407 .category = "^general$",
410 static struct aco_file agents_conf = {
411 .filename = "agents.conf",
412 .types = ACO_TYPES(&general_type, &agent_type),
415 static AO2_GLOBAL_OBJ_STATIC(cfg_handle);
417 static void agents_cfg_destructor(void *vdoomed)
419 struct agents_cfg *doomed = vdoomed;
421 ao2_cleanup(doomed->agents);
422 doomed->agents = NULL;
427 * \brief Create struct agents_cfg object.
430 * \note A lock is not needed for the object or any secondary
431 * created cfg objects. These objects are immutable after the
432 * config is loaded and applied.
434 * \retval New struct agents_cfg object.
435 * \retval NULL on error.
437 static void *agents_cfg_alloc(void)
439 struct agents_cfg *cfg;
441 cfg = ao2_alloc_options(sizeof(*cfg), agents_cfg_destructor,
442 AO2_ALLOC_OPT_LOCK_NOLOCK);
446 cfg->agents = ao2_container_alloc_rbtree(AO2_ALLOC_OPT_LOCK_NOLOCK,
447 AO2_CONTAINER_ALLOC_OPT_DUPS_REJECT, agent_cfg_sort_cmp, NULL);
455 static void agents_post_apply_config(void);
457 CONFIG_INFO_STANDARD(cfg_info, cfg_handle, agents_cfg_alloc,
458 .files = ACO_FILES(&agents_conf),
459 .post_apply_config = agents_post_apply_config,
462 static void destroy_config(void)
464 ao2_global_obj_release(cfg_handle);
465 aco_info_destroy(&cfg_info);
468 static int load_config(void)
470 if (aco_info_init(&cfg_info)) {
475 aco_option_register(&cfg_info, "ackcall", ACO_EXACT, agent_types, "no", OPT_BOOL_T, 1, FLDSET(struct agent_cfg, ack_call));
476 aco_option_register(&cfg_info, "acceptdtmf", ACO_EXACT, agent_types, "#", OPT_STRINGFIELD_T, 1, STRFLDSET(struct agent_cfg, dtmf_accept));
477 aco_option_register(&cfg_info, "autologoff", ACO_EXACT, agent_types, "0", OPT_UINT_T, 0, FLDSET(struct agent_cfg, auto_logoff));
478 aco_option_register(&cfg_info, "wrapuptime", ACO_EXACT, agent_types, "0", OPT_UINT_T, 0, FLDSET(struct agent_cfg, wrapup_time));
479 aco_option_register(&cfg_info, "musiconhold", ACO_EXACT, agent_types, "default", OPT_STRINGFIELD_T, 0, STRFLDSET(struct agent_cfg, moh));
480 aco_option_register(&cfg_info, "recordagentcalls", ACO_EXACT, agent_types, "no", OPT_BOOL_T, 1, FLDSET(struct agent_cfg, record_agent_calls));
481 aco_option_register(&cfg_info, "custom_beep", ACO_EXACT, agent_types, "beep", OPT_STRINGFIELD_T, 1, STRFLDSET(struct agent_cfg, beep_sound));
482 aco_option_register(&cfg_info, "fullname", ACO_EXACT, agent_types, NULL, OPT_STRINGFIELD_T, 0, STRFLDSET(struct agent_cfg, full_name));
484 if (aco_process_config(&cfg_info, 0) == ACO_PROCESS_ERROR) {
496 /*! The agent is defined but an agent is not present. */
497 AGENT_STATE_LOGGED_OUT,
498 /*! Forced initial login wait to allow any local channel optimizations to happen. */
499 AGENT_STATE_PROBATION_WAIT,
500 /*! The agent is ready for a call. */
501 AGENT_STATE_READY_FOR_CALL,
502 /*! The agent has a call waiting to connect. */
503 AGENT_STATE_CALL_PRESENT,
504 /*! The agent needs to ack the call. */
505 AGENT_STATE_CALL_WAIT_ACK,
506 /*! The agent is connected with a call. */
508 /*! The agent is resting between calls. */
509 AGENT_STATE_CALL_WRAPUP,
510 /*! The agent is being kicked out. */
511 AGENT_STATE_LOGGING_OUT,
514 /*! Agent config option override flags. */
515 enum agent_override_flags {
516 AGENT_FLAG_ACK_CALL = (1 << 0),
517 AGENT_FLAG_DTMF_ACCEPT = (1 << 1),
518 AGENT_FLAG_AUTO_LOGOFF = (1 << 2),
519 AGENT_FLAG_WRAPUP_TIME = (1 << 3),
522 /*! \brief Structure representing an agent. */
524 AST_DECLARE_STRING_FIELDS(
525 /*! Identification of the agent. (agents container key) */
526 AST_STRING_FIELD(username);
527 /*! Login override DTMF string for an agent to accept a call. */
528 AST_STRING_FIELD(override_dtmf_accept);
530 /*! Connected line information to send when reentering the holding bridge. */
531 struct ast_party_connected_line waiting_colp;
532 /*! Flags show if settings were overridden by channel vars. */
534 /*! Login override number of seconds for agent to ack a call before being logged off. */
535 unsigned int override_auto_logoff;
536 /*! Login override time after a call in ms before the agent can get a new call. */
537 unsigned int override_wrapup_time;
538 /*! Login override if agent needs to ack a call to accept it. */
539 unsigned int override_ack_call:1;
541 /*! TRUE if the agent is requested to logoff when the current call ends. */
542 unsigned int deferred_logoff:1;
544 /*! Mark and sweep config update to determine if an agent is dead. */
545 unsigned int the_mark:1;
547 * \brief TRUE if the agent is no longer configured and is being destroyed.
549 * \note Agents cannot log in if they are dead.
553 /*! Agent control state variable. */
554 enum agent_state state;
555 /*! Custom device state of agent. */
556 enum ast_device_state devstate;
558 /*! When agent first logged in */
560 /*! When agent login probation started. */
561 time_t probation_start;
562 /*! When call started */
564 /*! When ack timer started */
565 struct timeval ack_time;
566 /*! When last disconnected */
567 struct timeval last_disconnect;
569 /*! Caller is waiting in this bridge for agent to join. (Holds ref) */
570 struct ast_bridge *caller_bridge;
571 /*! Agent is logged in with this channel. (Holds ref) (NULL if not logged in.) */
572 struct ast_channel *logged;
573 /*! Active config values from config file. (Holds ref) */
574 struct agent_cfg *cfg;
577 /*! Container of defined agents. */
578 static struct ao2_container *agents;
581 * \brief Lock the agent.
583 * \param agent Agent to lock
587 #define agent_lock(agent) _agent_lock(agent, __FILE__, __PRETTY_FUNCTION__, __LINE__, #agent)
588 static inline void _agent_lock(struct agent_pvt *agent, const char *file, const char *function, int line, const char *var)
590 __ao2_lock(agent, AO2_LOCK_REQ_MUTEX, file, function, line, var);
594 * \brief Unlock the agent.
596 * \param agent Agent to unlock
600 #define agent_unlock(agent) _agent_unlock(agent, __FILE__, __PRETTY_FUNCTION__, __LINE__, #agent)
601 static inline void _agent_unlock(struct agent_pvt *agent, const char *file, const char *function, int line, const char *var)
603 __ao2_unlock(agent, file, function, line, var);
608 * \brief Obtain the agent logged in channel lock if it exists.
611 * \param agent Pointer to the LOCKED agent_pvt.
613 * \note Assumes the agent lock is already obtained.
617 static struct ast_channel *agent_lock_logged(struct agent_pvt *agent)
619 struct ast_channel *logged;
622 if (!agent->logged) { /* No owner. Nothing to do. */
626 /* If we don't ref the logged, it could be killed when we unlock the agent. */
627 logged = ast_channel_ref(agent->logged);
629 /* Locking logged requires us to lock channel, then agent. */
631 ast_channel_lock(logged);
634 /* Check if logged changed during agent unlock period */
635 if (logged != agent->logged) {
636 /* Channel changed. Unref and do another pass. */
637 ast_channel_unlock(logged);
638 ast_channel_unref(logged);
640 /* Channel stayed the same. Return it. */
648 * \brief Get the Agent:agent_id device state.
651 * \param agent_id Username of the agent.
654 * Search the agents container for the agent and return the
657 * \return Device state of the agent.
659 static enum ast_device_state agent_pvt_devstate_get(const char *agent_id)
661 RAII_VAR(struct agent_pvt *, agent, ao2_find(agents, agent_id, OBJ_KEY), ao2_cleanup);
664 return agent->devstate;
666 return AST_DEVICE_INVALID;
671 * \brief Request an agent device state be updated.
674 * \param agent_id Which agent needs the device state updated.
678 static void agent_devstate_changed(const char *agent_id)
680 ast_devstate_changed(AST_DEVICE_UNKNOWN, AST_DEVSTATE_CACHABLE, "Agent:%s", agent_id);
683 static void agent_pvt_destructor(void *vdoomed)
685 struct agent_pvt *doomed = vdoomed;
687 /* Make sure device state reflects agent destruction. */
688 if (!ast_strlen_zero(doomed->username)) {
689 ast_debug(1, "Agent %s: Destroyed.\n", doomed->username);
690 agent_devstate_changed(doomed->username);
693 ast_party_connected_line_free(&doomed->waiting_colp);
694 if (doomed->caller_bridge) {
695 ast_bridge_destroy(doomed->caller_bridge);
696 doomed->caller_bridge = NULL;
698 if (doomed->logged) {
699 doomed->logged = ast_channel_unref(doomed->logged);
701 ao2_cleanup(doomed->cfg);
703 ast_string_field_free_memory(doomed);
706 static struct agent_pvt *agent_pvt_new(struct agent_cfg *cfg)
708 struct agent_pvt *agent;
710 agent = ao2_alloc(sizeof(*agent), agent_pvt_destructor);
714 if (ast_string_field_init(agent, 32)) {
718 ast_string_field_set(agent, username, cfg->username);
719 ast_party_connected_line_init(&agent->waiting_colp);
722 agent->devstate = AST_DEVICE_UNAVAILABLE;
728 * \brief Agents ao2 container sort function.
731 * \param obj_left pointer to the (user-defined part) of an object.
732 * \param obj_right pointer to the (user-defined part) of an object.
733 * \param flags flags from ao2_callback()
734 * OBJ_POINTER - if set, 'obj_right', is an object.
735 * OBJ_KEY - if set, 'obj_right', is a search key item that is not an object.
736 * OBJ_PARTIAL_KEY - if set, 'obj_right', is a partial search key item that is not an object.
738 * \retval <0 if obj_left < obj_right
739 * \retval =0 if obj_left == obj_right
740 * \retval >0 if obj_left > obj_right
742 static int agent_pvt_sort_cmp(const void *obj_left, const void *obj_right, int flags)
744 const struct agent_pvt *agent_left = obj_left;
745 const struct agent_pvt *agent_right = obj_right;
746 const char *right_key = obj_right;
749 switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
752 right_key = agent_right->username;
755 cmp = strcmp(agent_left->username, right_key);
757 case OBJ_PARTIAL_KEY:
758 cmp = strncmp(agent_left->username, right_key, strlen(right_key));
766 * \brief ao2_find() callback function.
770 * found = ao2_find(agents, agent, OBJ_POINTER);
771 * found = ao2_find(agents, "agent-id", OBJ_KEY);
772 * found = ao2_find(agents, agent->logged, 0);
774 static int agent_pvt_cmp(void *obj, void *arg, int flags)
776 const struct agent_pvt *agent = obj;
779 switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
782 case OBJ_PARTIAL_KEY:
786 if (agent->logged == arg) {
796 static int agent_mark(void *obj, void *arg, int flags)
798 struct agent_pvt *agent = obj;
806 static void agents_mark(void)
808 ao2_callback(agents, 0, agent_mark, NULL);
811 static int agent_sweep(void *obj, void *arg, int flags)
813 struct agent_pvt *agent = obj;
817 if (agent->the_mark) {
820 /* Unlink dead agents immediately. */
827 static void agents_sweep(void)
829 struct ao2_iterator *iter;
830 struct agent_pvt *agent;
831 struct ast_channel *logged;
833 iter = ao2_callback(agents, OBJ_MULTIPLE | OBJ_UNLINK, agent_sweep, NULL);
837 for (; (agent = ao2_iterator_next(iter)); ao2_ref(agent, -1)) {
840 logged = ast_channel_ref(agent->logged);
849 "Forced logoff of agent %s(%s). Agent no longer configured.\n",
850 agent->username, ast_channel_name(logged));
851 ast_softhangup(logged, AST_SOFTHANGUP_EXPLICIT);
852 ast_channel_unref(logged);
854 ao2_iterator_destroy(iter);
857 static void agents_post_apply_config(void)
859 struct ao2_iterator iter;
860 struct agent_cfg *cfg;
861 RAII_VAR(struct agents_cfg *, cfgs, ao2_global_obj_ref(cfg_handle), ao2_cleanup);
863 ast_assert(cfgs != NULL);
866 iter = ao2_iterator_init(cfgs->agents, 0);
867 for (; (cfg = ao2_iterator_next(&iter)); ao2_ref(cfg, -1)) {
868 RAII_VAR(struct agent_pvt *, agent, ao2_find(agents, cfg->username, OBJ_KEY), ao2_cleanup);
873 if (!agent->logged) {
874 struct agent_cfg *cfg_old;
876 /* Replace the config of agents not logged in. */
877 cfg_old = agent->cfg;
880 ao2_cleanup(cfg_old);
885 agent = agent_pvt_new(cfg);
889 ao2_link(agents, agent);
890 ast_debug(1, "Agent %s: Created.\n", agent->username);
891 agent_devstate_changed(agent->username);
893 ao2_iterator_destroy(&iter);
897 static int agent_logoff_request(const char *agent_id, int soft)
899 struct ast_channel *logged;
900 RAII_VAR(struct agent_pvt *, agent, ao2_find(agents, agent_id, OBJ_KEY), ao2_cleanup);
907 logged = agent_lock_logged(agent);
910 agent->deferred_logoff = 1;
912 ast_softhangup(logged, AST_SOFTHANGUP_EXPLICIT);
914 ast_channel_unlock(logged);
915 ast_channel_unref(logged);
921 /*! Agent holding bridge instance holder. */
922 static AO2_GLOBAL_OBJ_STATIC(agent_holding);
924 /*! Agent holding bridge deferred creation lock. */
925 AST_MUTEX_DEFINE_STATIC(agent_holding_lock);
929 * \brief Connect the agent with the waiting caller.
932 * \param bridge_channel Agent channel connecting to the caller.
933 * \param agent Which agent is connecting to the caller.
935 * \note The agent is locked on entry and not locked on exit.
939 static void agent_connect_caller(struct ast_bridge_channel *bridge_channel, struct agent_pvt *agent)
941 struct ast_bridge *caller_bridge;
942 int record_agent_calls;
945 record_agent_calls = agent->cfg->record_agent_calls;
946 caller_bridge = agent->caller_bridge;
947 agent->caller_bridge = NULL;
948 agent->state = AGENT_STATE_ON_CALL;
949 time(&agent->call_start);
952 if (!caller_bridge) {
954 ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END);
957 res = ast_bridge_move(caller_bridge, bridge_channel->bridge, bridge_channel->chan,
961 ast_bridge_destroy(caller_bridge);
962 ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END);
965 ast_bridge_channel_write_control_data(bridge_channel, AST_CONTROL_ANSWER, NULL, 0);
967 if (record_agent_calls) {
968 struct ast_bridge_features_automixmonitor options = {
969 .start_stop = AUTO_MONITOR_START,
973 * The agent is in the new bridge so we can invoke the
974 * mixmonitor hook to only start recording.
976 ast_bridge_features_do(AST_BRIDGE_BUILTIN_AUTOMIXMON, caller_bridge,
977 bridge_channel, &options);
981 static int bridge_agent_hold_ack(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
983 struct agent_pvt *agent = hook_pvt;
986 switch (agent->state) {
987 case AGENT_STATE_CALL_WAIT_ACK:
988 /* Connect to caller now. */
989 ast_debug(1, "Agent %s: Acked call.\n", agent->username);
990 agent_connect_caller(bridge_channel, agent);/* Will unlock agent. */
999 static int bridge_agent_hold_heartbeat(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
1001 struct agent_pvt *agent = hook_pvt;
1002 int probation_timedout = 0;
1003 int ack_timedout = 0;
1004 int wrapup_timedout = 0;
1005 int deferred_logoff;
1006 unsigned int wrapup_time;
1007 unsigned int auto_logoff;
1010 deferred_logoff = agent->deferred_logoff;
1011 if (deferred_logoff) {
1012 agent->state = AGENT_STATE_LOGGING_OUT;
1015 switch (agent->state) {
1016 case AGENT_STATE_PROBATION_WAIT:
1017 probation_timedout =
1018 LOGIN_WAIT_TIMEOUT_TIME <= (time(NULL) - agent->probation_start);
1019 if (probation_timedout) {
1020 /* Now ready for a caller. */
1021 agent->state = AGENT_STATE_READY_FOR_CALL;
1022 agent->devstate = AST_DEVICE_NOT_INUSE;
1025 case AGENT_STATE_CALL_WAIT_ACK:
1026 /* Check ack call time. */
1027 auto_logoff = agent->cfg->auto_logoff;
1028 if (ast_test_flag(agent, AGENT_FLAG_AUTO_LOGOFF)) {
1029 auto_logoff = agent->override_auto_logoff;
1032 auto_logoff *= 1000;
1033 ack_timedout = ast_tvdiff_ms(ast_tvnow(), agent->ack_time) > auto_logoff;
1035 agent->state = AGENT_STATE_LOGGING_OUT;
1039 case AGENT_STATE_CALL_WRAPUP:
1040 /* Check wrapup time. */
1041 wrapup_time = agent->cfg->wrapup_time;
1042 if (ast_test_flag(agent, AGENT_FLAG_WRAPUP_TIME)) {
1043 wrapup_time = agent->override_wrapup_time;
1045 wrapup_timedout = ast_tvdiff_ms(ast_tvnow(), agent->last_disconnect) > wrapup_time;
1046 if (wrapup_timedout) {
1047 agent->state = AGENT_STATE_READY_FOR_CALL;
1048 agent->devstate = AST_DEVICE_NOT_INUSE;
1054 agent_unlock(agent);
1056 if (deferred_logoff) {
1057 ast_debug(1, "Agent %s: Deferred logoff.\n", agent->username);
1058 ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END);
1059 } else if (probation_timedout) {
1060 ast_debug(1, "Agent %s: Login complete.\n", agent->username);
1061 agent_devstate_changed(agent->username);
1062 } else if (ack_timedout) {
1063 ast_debug(1, "Agent %s: Ack call timeout.\n", agent->username);
1064 ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END);
1065 } else if (wrapup_timedout) {
1066 ast_debug(1, "Agent %s: Wrapup timeout. Ready for new call.\n", agent->username);
1067 agent_devstate_changed(agent->username);
1073 static void agent_after_bridge_cb(struct ast_channel *chan, void *data);
1074 static void agent_after_bridge_cb_failed(enum ast_after_bridge_cb_reason reason, void *data);
1078 * \brief ast_bridge agent_hold push method.
1081 * \param self Bridge to operate upon.
1082 * \param bridge_channel Bridge channel to push.
1083 * \param swap Bridge channel to swap places with if not NULL.
1085 * \note On entry, self is already locked.
1087 * \retval 0 on success
1088 * \retval -1 on failure
1090 static int bridge_agent_hold_push(struct ast_bridge *self, struct ast_bridge_channel *bridge_channel, struct ast_bridge_channel *swap)
1093 unsigned int wrapup_time;
1094 char dtmf[AST_FEATURE_MAX_LEN];
1095 struct ast_channel *chan;
1096 const char *moh_class;
1097 RAII_VAR(struct agent_pvt *, agent, NULL, ao2_cleanup);
1099 chan = bridge_channel->chan;
1101 agent = ao2_find(agents, swap ? swap->chan : chan, 0);
1103 /* Could not find the agent. */
1107 /* Setup agent entertainment */
1109 moh_class = ast_strdupa(agent->cfg->moh);
1110 agent_unlock(agent);
1111 res |= ast_channel_add_bridge_role(chan, "holding_participant");
1112 res |= ast_channel_set_bridge_role_option(chan, "holding_participant", "idle_mode", "musiconhold");
1113 res |= ast_channel_set_bridge_role_option(chan, "holding_participant", "moh_class", moh_class);
1115 /* Add DTMF acknowledge hook. */
1118 if (ast_test_flag(agent, AGENT_FLAG_ACK_CALL)
1119 ? agent->override_ack_call : agent->cfg->ack_call) {
1120 const char *dtmf_accept;
1122 dtmf_accept = ast_test_flag(agent, AGENT_FLAG_DTMF_ACCEPT)
1123 ? agent->override_dtmf_accept : agent->cfg->dtmf_accept;
1124 ast_copy_string(dtmf, dtmf_accept, sizeof(dtmf));
1126 agent_unlock(agent);
1127 if (!ast_strlen_zero(dtmf)) {
1129 if (ast_bridge_dtmf_hook(bridge_channel->features, dtmf, bridge_agent_hold_ack,
1130 agent, __ao2_cleanup, AST_BRIDGE_HOOK_REMOVE_ON_PULL)) {
1136 /* Add heartbeat interval hook. */
1138 if (ast_bridge_interval_hook(bridge_channel->features, 1000,
1139 bridge_agent_hold_heartbeat, agent, __ao2_cleanup, AST_BRIDGE_HOOK_REMOVE_ON_PULL)) {
1144 res |= ast_bridge_base_v_table.push(self, bridge_channel, swap);
1146 ast_channel_remove_bridge_role(chan, "holding_participant");
1151 res = ast_after_bridge_callback_set(chan, agent_after_bridge_cb,
1152 agent_after_bridge_cb_failed, chan);
1154 ast_channel_remove_bridge_role(chan, "holding_participant");
1159 ast_channel_unref(agent->logged);
1160 agent->logged = ast_channel_ref(chan);
1161 agent_unlock(agent);
1164 * Kick the channel out so it can come back in fully controlled.
1165 * Otherwise, the after bridge callback will linger and the
1166 * agent will have some slightly different behavior in corner
1169 ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END);
1174 switch (agent->state) {
1175 case AGENT_STATE_LOGGED_OUT:
1177 * \todo XXX the login probation time should be only if it is needed.
1179 * Need to determine if there are any local channels that can
1180 * optimize and wait until they actually do before leaving the
1181 * AGENT_STATE_PROBATION_WAIT state. For now, the blind
1182 * timer of LOGIN_WAIT_TIMEOUT_TIME will do.
1185 * Start the login probation timer.
1187 * We cannot handle an agent local channel optimization when the
1188 * agent is on a call. The optimization may kick the agent
1189 * channel we know about out of the call without our being able
1190 * to switch to the replacement channel. Get any agent local
1191 * channel optimization out of the way while the agent is in the
1194 time(&agent->probation_start);
1195 agent->state = AGENT_STATE_PROBATION_WAIT;
1196 agent_unlock(agent);
1198 case AGENT_STATE_PROBATION_WAIT:
1199 /* Restart the probation timer. */
1200 time(&agent->probation_start);
1201 agent_unlock(agent);
1203 case AGENT_STATE_READY_FOR_CALL:
1205 * Likely someone manally kicked us out of the holding bridge
1206 * and we came right back in.
1208 agent_unlock(agent);
1211 /* Unexpected agent state. */
1214 case AGENT_STATE_CALL_PRESENT:
1215 case AGENT_STATE_CALL_WAIT_ACK:
1216 agent->state = AGENT_STATE_READY_FOR_CALL;
1217 agent->devstate = AST_DEVICE_NOT_INUSE;
1218 agent_unlock(agent);
1219 ast_debug(1, "Agent %s: Call abort recovery complete.\n", agent->username);
1220 agent_devstate_changed(agent->username);
1222 case AGENT_STATE_ON_CALL:
1223 case AGENT_STATE_CALL_WRAPUP:
1224 wrapup_time = agent->cfg->wrapup_time;
1225 if (ast_test_flag(agent, AGENT_FLAG_WRAPUP_TIME)) {
1226 wrapup_time = agent->override_wrapup_time;
1229 agent->state = AGENT_STATE_CALL_WRAPUP;
1231 agent->state = AGENT_STATE_READY_FOR_CALL;
1232 agent->devstate = AST_DEVICE_NOT_INUSE;
1234 agent_unlock(agent);
1236 /* No wrapup time. */
1237 ast_debug(1, "Agent %s: Ready for new call.\n", agent->username);
1238 agent_devstate_changed(agent->username);
1248 * \brief ast_bridge agent_hold pull method.
1250 * \param self Bridge to operate upon.
1251 * \param bridge_channel Bridge channel to pull.
1254 * Remove any channel hooks controlled by the bridge. Release
1255 * any resources held by bridge_channel->bridge_pvt and release
1256 * bridge_channel->bridge_pvt.
1258 * \note On entry, self is already locked.
1262 static void bridge_agent_hold_pull(struct ast_bridge *self, struct ast_bridge_channel *bridge_channel)
1264 ast_channel_remove_bridge_role(bridge_channel->chan, "holding_participant");
1265 ast_bridge_base_v_table.pull(self, bridge_channel);
1269 * \brief The bridge is being dissolved.
1271 * \param self Bridge to operate upon.
1274 * The bridge is being dissolved. Remove any external
1275 * references to the bridge so it can be destroyed.
1277 * \note On entry, self must NOT be locked.
1281 static void bridge_agent_hold_dissolving(struct ast_bridge *self)
1283 ao2_global_obj_replace_unref(agent_holding, NULL);
1284 ast_bridge_base_v_table.dissolving(self);
1287 static struct ast_bridge_methods bridge_agent_hold_v_table;
1289 static struct ast_bridge *bridge_agent_hold_new(void)
1291 struct ast_bridge *bridge;
1293 bridge = ast_bridge_alloc(sizeof(struct ast_bridge), &bridge_agent_hold_v_table);
1294 bridge = ast_bridge_base_init(bridge, AST_BRIDGE_CAPABILITY_HOLDING,
1295 AST_BRIDGE_FLAG_MERGE_INHIBIT_TO | AST_BRIDGE_FLAG_MERGE_INHIBIT_FROM
1296 | AST_BRIDGE_FLAG_SWAP_INHIBIT_FROM | AST_BRIDGE_FLAG_TRANSFER_PROHIBITED);
1297 bridge = ast_bridge_register(bridge);
1301 static void bridging_init_agent_hold(void)
1303 /* Setup bridge agent_hold subclass v_table. */
1304 bridge_agent_hold_v_table = ast_bridge_base_v_table;
1305 bridge_agent_hold_v_table.name = "agent_hold";
1306 bridge_agent_hold_v_table.dissolving = bridge_agent_hold_dissolving;
1307 bridge_agent_hold_v_table.push = bridge_agent_hold_push;
1308 bridge_agent_hold_v_table.pull = bridge_agent_hold_pull;
1311 static int bridge_agent_hold_deferred_create(void)
1313 RAII_VAR(struct ast_bridge *, holding, ao2_global_obj_ref(agent_holding), ao2_cleanup);
1316 ast_mutex_lock(&agent_holding_lock);
1317 holding = ao2_global_obj_ref(agent_holding);
1319 holding = bridge_agent_hold_new();
1320 ao2_global_obj_replace_unref(agent_holding, holding);
1322 ast_mutex_unlock(&agent_holding_lock);
1324 ast_log(LOG_ERROR, "Could not create agent holding bridge.\n");
1331 static void send_agent_login(struct ast_channel *chan, const char *agent)
1333 RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
1335 ast_assert(agent != NULL);
1337 blob = ast_json_pack("{s: s}",
1343 ast_channel_publish_blob(chan, ast_channel_agent_login_type(), blob);
1346 static void send_agent_logoff(struct ast_channel *chan, const char *agent, long logintime)
1348 RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
1350 ast_assert(agent != NULL);
1352 blob = ast_json_pack("{s: s, s: i}",
1354 "logintime", logintime);
1359 ast_channel_publish_blob(chan, ast_channel_agent_logoff_type(), blob);
1364 * \brief Logout the agent.
1367 * \param agent Which agent logging out.
1369 * \note On entry agent is already locked. On exit it is no longer locked.
1373 static void agent_logout(struct agent_pvt *agent)
1375 struct ast_channel *logged;
1376 struct ast_bridge *caller_bridge;
1377 long time_logged_in;
1379 time_logged_in = time(NULL) - agent->login_start;
1380 logged = agent->logged;
1381 agent->logged = NULL;
1382 caller_bridge = agent->caller_bridge;
1383 caller_bridge = NULL;
1384 agent->state = AGENT_STATE_LOGGED_OUT;
1385 agent->devstate = AST_DEVICE_UNAVAILABLE;
1386 ast_clear_flag(agent, AST_FLAGS_ALL);
1387 agent_unlock(agent);
1388 agent_devstate_changed(agent->username);
1390 if (caller_bridge) {
1391 ast_bridge_destroy(caller_bridge);
1394 send_agent_logoff(logged, agent->username, time_logged_in);
1395 ast_verb(2, "Agent '%s' logged out. Logged in for %ld seconds.\n",
1396 agent->username, time_logged_in);
1397 ast_channel_unref(logged);
1402 * \brief Agent driver loop.
1405 * \param agent Which agent.
1406 * \param logged The logged in channel.
1410 static void agent_run(struct agent_pvt *agent, struct ast_channel *logged)
1412 struct ast_bridge_features features;
1414 if (!ast_bridge_features_init(&features)) {
1416 struct agents_cfg *cfgs;
1417 struct agent_cfg *cfg_new;
1418 struct agent_cfg *cfg_old;
1419 struct ast_bridge *holding;
1420 struct ast_bridge *caller_bridge;
1422 holding = ao2_global_obj_ref(agent_holding);
1427 ast_bridge_join(holding, logged, NULL, &features, NULL, 1);
1428 if (logged != agent->logged) {
1436 /* Update the agent's config before rejoining the holding bridge. */
1437 cfgs = ao2_global_obj_ref(cfg_handle);
1441 cfg_new = ao2_find(cfgs->agents, agent->username, OBJ_KEY);
1447 cfg_old = agent->cfg;
1448 agent->cfg = cfg_new;
1450 agent->last_disconnect = ast_tvnow();
1452 /* Clear out any caller bridge before rejoining the holding bridge. */
1453 caller_bridge = agent->caller_bridge;
1454 agent->caller_bridge = NULL;
1455 agent_unlock(agent);
1456 ao2_ref(cfg_old, -1);
1457 if (caller_bridge) {
1458 ast_bridge_destroy(caller_bridge);
1461 if (agent->state == AGENT_STATE_LOGGING_OUT
1462 || agent->deferred_logoff
1463 || ast_check_hangup_locked(logged)) {
1468 * It is safe to access agent->waiting_colp without a lock. It
1469 * is only setup on agent login and not changed.
1471 ast_channel_update_connected_line(logged, &agent->waiting_colp, NULL);
1473 ast_bridge_features_cleanup(&features);
1477 if (logged != agent->logged) {
1479 * We are no longer the agent channel because of local channel
1482 agent_unlock(agent);
1483 ast_debug(1, "Agent %s: Channel %s is no longer the agent.\n",
1484 agent->username, ast_channel_name(logged));
1487 agent_logout(agent);
1490 static void agent_after_bridge_cb(struct ast_channel *chan, void *data)
1492 struct agent_pvt *agent;
1494 agent = ao2_find(agents, chan, 0);
1499 ast_debug(1, "Agent %s: New agent channel %s.\n",
1500 agent->username, ast_channel_name(chan));
1501 agent_run(agent, chan);
1505 static void agent_after_bridge_cb_failed(enum ast_after_bridge_cb_reason reason, void *data)
1507 struct ast_channel *chan = data;
1508 struct agent_pvt *agent;
1510 agent = ao2_find(agents, chan, 0);
1514 ast_log(LOG_WARNING, "Agent %s: Forced logout. Lost control of %s because: %s\n",
1515 agent->username, ast_channel_name(chan),
1516 ast_after_bridge_cb_reason_string(reason));
1518 agent_logout(agent);
1524 * \brief Get the lock on the agent bridge_channel and return it.
1527 * \param agent Whose bridge_chanel to get.
1529 * \retval bridge_channel on success (Reffed and locked).
1530 * \retval NULL on error.
1532 static struct ast_bridge_channel *agent_bridge_channel_get_lock(struct agent_pvt *agent)
1534 struct ast_channel *logged;
1535 struct ast_bridge_channel *bc;
1539 logged = agent->logged;
1541 agent_unlock(agent);
1544 ast_channel_ref(logged);
1545 agent_unlock(agent);
1547 ast_channel_lock(logged);
1548 bc = ast_channel_get_bridge_channel(logged);
1549 ast_channel_unlock(logged);
1550 ast_channel_unref(logged);
1552 if (agent->logged != logged) {
1558 ast_bridge_channel_lock(bc);
1559 if (bc->chan != logged || agent->logged != logged) {
1560 ast_bridge_channel_unlock(bc);
1568 static void caller_abort_agent(struct agent_pvt *agent)
1570 struct ast_bridge_channel *logged;
1572 logged = agent_bridge_channel_get_lock(agent);
1574 struct ast_bridge *caller_bridge;
1576 ast_debug(1, "Agent '%s' no longer logged in.\n", agent->username);
1579 caller_bridge = agent->caller_bridge;
1580 agent->caller_bridge = NULL;
1581 agent_unlock(agent);
1582 if (caller_bridge) {
1583 ast_bridge_destroy(caller_bridge);
1588 /* Kick the agent out of the holding bridge to reset it. */
1589 ast_bridge_change_state_nolock(logged, AST_BRIDGE_CHANNEL_STATE_END);
1590 ast_bridge_channel_unlock(logged);
1593 static int caller_safety_timeout(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
1595 struct agent_pvt *agent = hook_pvt;
1597 if (agent->state == AGENT_STATE_CALL_PRESENT) {
1598 ast_verb(3, "Agent '%s' did not respond. Safety timeout.\n", agent->username);
1599 ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END);
1600 caller_abort_agent(agent);
1606 static void agent_alert(struct ast_bridge_channel *bridge_channel, const void *payload, size_t payload_size)
1608 const char *agent_id = payload;
1609 const char *playfile;
1610 RAII_VAR(struct agent_pvt *, agent, NULL, ao2_cleanup);
1612 agent = ao2_find(agents, agent_id, OBJ_KEY);
1614 ast_debug(1, "Agent '%s' does not exist. Where did it go?\n", agent_id);
1618 /* Alert the agent. */
1620 playfile = ast_strdupa(agent->cfg->beep_sound);
1621 agent_unlock(agent);
1622 ast_stream_and_wait(bridge_channel->chan, playfile, AST_DIGIT_NONE);
1625 switch (agent->state) {
1626 case AGENT_STATE_CALL_PRESENT:
1627 if (ast_test_flag(agent, AGENT_FLAG_ACK_CALL)
1628 ? agent->override_ack_call : agent->cfg->ack_call) {
1629 agent->state = AGENT_STATE_CALL_WAIT_ACK;
1630 agent->ack_time = ast_tvnow();
1634 /* Connect to caller now. */
1635 ast_debug(1, "Agent %s: Immediately connecting to call.\n", agent->username);
1636 agent_connect_caller(bridge_channel, agent);/* Will unlock agent. */
1641 agent_unlock(agent);
1644 static int send_alert_to_agent(struct ast_bridge_channel *bridge_channel, const char *agent_id)
1646 return ast_bridge_channel_queue_callback(bridge_channel, agent_alert, agent_id,
1647 strlen(agent_id) + 1);
1650 static int send_colp_to_agent(struct ast_bridge_channel *bridge_channel, struct ast_party_connected_line *connected)
1652 struct ast_set_party_connected_line update = {
1657 unsigned char data[1024]; /* This should be large enough */
1660 datalen = ast_connected_line_build_data(data, sizeof(data), connected, &update);
1661 if (datalen == (size_t) -1) {
1665 return ast_bridge_channel_queue_control_data(bridge_channel,
1666 AST_CONTROL_CONNECTED_LINE, data, datalen);
1670 * \brief Dialplan AgentRequest application to locate an agent to talk with.
1672 * \param chan Channel wanting to talk with an agent.
1673 * \param data Application parameters
1675 * \retval 0 To continue in dialplan.
1676 * \retval -1 To hangup.
1678 static int agent_request_exec(struct ast_channel *chan, const char *data)
1680 struct ast_bridge *caller_bridge;
1681 struct ast_bridge_channel *logged;
1684 struct ast_bridge_features caller_features;
1685 struct ast_party_connected_line connected;
1686 AST_DECLARE_APP_ARGS(args,
1687 AST_APP_ARG(agent_id);
1688 AST_APP_ARG(other); /* Any remaining unused arguments */
1691 RAII_VAR(struct agent_pvt *, agent, NULL, ao2_cleanup);
1693 if (bridge_agent_hold_deferred_create()) {
1697 parse = ast_strdupa(data ?: "");
1698 AST_STANDARD_APP_ARGS(args, parse);
1700 if (ast_strlen_zero(args.agent_id)) {
1701 ast_log(LOG_WARNING, "AgentRequest requires an AgentId\n");
1705 /* Find the agent. */
1706 agent = ao2_find(agents, args.agent_id, OBJ_KEY);
1708 ast_verb(3, "Agent '%s' does not exist.\n", args.agent_id);
1709 pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "INVALID");
1713 if (ast_bridge_features_init(&caller_features)) {
1717 /* Add safety timeout hook. */
1719 if (ast_bridge_interval_hook(&caller_features, CALLER_SAFETY_TIMEOUT_TIME,
1720 caller_safety_timeout, agent, __ao2_cleanup, AST_BRIDGE_HOOK_REMOVE_ON_PULL)) {
1722 ast_bridge_features_cleanup(&caller_features);
1726 caller_bridge = ast_bridge_basic_new();
1727 if (!caller_bridge) {
1728 ast_bridge_features_cleanup(&caller_features);
1732 /* Get COLP for agent. */
1733 ast_party_connected_line_init(&connected);
1734 ast_channel_lock(chan);
1735 ast_connected_line_copy_from_caller(&connected, ast_channel_caller(chan));
1736 ast_channel_unlock(chan);
1739 switch (agent->state) {
1740 case AGENT_STATE_LOGGED_OUT:
1741 case AGENT_STATE_LOGGING_OUT:
1742 agent_unlock(agent);
1743 ast_party_connected_line_free(&connected);
1744 ast_bridge_destroy(caller_bridge);
1745 ast_bridge_features_cleanup(&caller_features);
1746 ast_verb(3, "Agent '%s' not logged in.\n", agent->username);
1747 pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "NOT_LOGGED_IN");
1749 case AGENT_STATE_READY_FOR_CALL:
1750 ao2_ref(caller_bridge, +1);
1751 agent->caller_bridge = caller_bridge;
1752 agent->state = AGENT_STATE_CALL_PRESENT;
1753 agent->devstate = AST_DEVICE_INUSE;
1756 agent_unlock(agent);
1757 ast_party_connected_line_free(&connected);
1758 ast_bridge_destroy(caller_bridge);
1759 ast_bridge_features_cleanup(&caller_features);
1760 ast_verb(3, "Agent '%s' is busy.\n", agent->username);
1761 pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "BUSY");
1764 agent_unlock(agent);
1765 agent_devstate_changed(agent->username);
1767 logged = agent_bridge_channel_get_lock(agent);
1769 ast_party_connected_line_free(&connected);
1770 ast_bridge_destroy(caller_bridge);
1771 ast_bridge_features_cleanup(&caller_features);
1772 ast_verb(3, "Agent '%s' not logged in.\n", agent->username);
1773 pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "NOT_LOGGED_IN");
1774 caller_abort_agent(agent);
1778 send_colp_to_agent(logged, &connected);
1779 ast_party_connected_line_free(&connected);
1781 res = send_alert_to_agent(logged, agent->username);
1782 ast_bridge_channel_unlock(logged);
1783 ao2_ref(logged, -1);
1785 ast_bridge_destroy(caller_bridge);
1786 ast_bridge_features_cleanup(&caller_features);
1787 ast_verb(3, "Agent '%s': Failed to alert the agent.\n", agent->username);
1788 pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "ERROR");
1789 caller_abort_agent(agent);
1793 ast_indicate(chan, AST_CONTROL_RINGING);
1794 ast_bridge_join(caller_bridge, chan, NULL, &caller_features, NULL, 1);
1795 ast_bridge_features_cleanup(&caller_features);
1802 * \brief Get agent config values from the login channel.
1805 * \param agent What to setup channel config values on.
1806 * \param chan Channel logging in as an agent.
1810 static void agent_login_channel_config(struct agent_pvt *agent, struct ast_channel *chan)
1812 struct ast_flags opts = { 0 };
1813 struct ast_party_connected_line connected;
1814 unsigned int override_ack_call = 0;
1815 unsigned int override_auto_logoff = 0;
1816 unsigned int override_wrapup_time = 0;
1817 const char *override_dtmf_accept = NULL;
1820 ast_party_connected_line_init(&connected);
1822 /* Get config values from channel. */
1823 ast_channel_lock(chan);
1824 ast_party_connected_line_copy(&connected, ast_channel_connected(chan));
1826 var = pbx_builtin_getvar_helper(chan, "AGENTACKCALL");
1827 if (!ast_strlen_zero(var)) {
1828 override_ack_call = ast_true(var) ? 1 : 0;
1829 ast_set_flag(&opts, AGENT_FLAG_ACK_CALL);
1832 var = pbx_builtin_getvar_helper(chan, "AGENTACCEPTDTMF");
1833 if (!ast_strlen_zero(var)) {
1834 override_dtmf_accept = ast_strdupa(var);
1835 ast_set_flag(&opts, AGENT_FLAG_DTMF_ACCEPT);
1838 var = pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF");
1839 if (!ast_strlen_zero(var)) {
1840 if (sscanf(var, "%u", &override_auto_logoff) == 1) {
1841 ast_set_flag(&opts, AGENT_FLAG_AUTO_LOGOFF);
1845 var = pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME");
1846 if (!ast_strlen_zero(var)) {
1847 if (sscanf(var, "%u", &override_wrapup_time) == 1) {
1848 ast_set_flag(&opts, AGENT_FLAG_WRAPUP_TIME);
1851 ast_channel_unlock(chan);
1853 /* Set config values on agent. */
1855 ast_party_connected_line_free(&agent->waiting_colp);
1856 agent->waiting_colp = connected;
1858 ast_string_field_set(agent, override_dtmf_accept, override_dtmf_accept);
1859 ast_copy_flags(agent, &opts, AST_FLAGS_ALL);
1860 agent->override_auto_logoff = override_auto_logoff;
1861 agent->override_wrapup_time = override_wrapup_time;
1862 agent->override_ack_call = override_ack_call;
1863 agent_unlock(agent);
1866 enum AGENT_LOGIN_OPT_FLAGS {
1867 OPT_SILENT = (1 << 0),
1869 AST_APP_OPTIONS(agent_login_opts, BEGIN_OPTIONS
1870 AST_APP_OPTION('s', OPT_SILENT),
1874 * \brief Dialplan AgentLogin application to log in an agent.
1876 * \param chan Channel attempting to login as an agent.
1877 * \param data Application parameters
1879 * \retval 0 To continue in dialplan.
1880 * \retval -1 To hangup.
1882 static int agent_login_exec(struct ast_channel *chan, const char *data)
1885 struct ast_flags opts;
1886 AST_DECLARE_APP_ARGS(args,
1887 AST_APP_ARG(agent_id);
1888 AST_APP_ARG(options);
1889 AST_APP_ARG(other); /* Any remaining unused arguments */
1892 RAII_VAR(struct agent_pvt *, agent, NULL, ao2_cleanup);
1894 if (bridge_agent_hold_deferred_create()) {
1898 if (ast_channel_state(chan) != AST_STATE_UP && ast_answer(chan)) {
1902 parse = ast_strdupa(data ?: "");
1903 AST_STANDARD_APP_ARGS(args, parse);
1905 if (ast_strlen_zero(args.agent_id)) {
1906 ast_log(LOG_WARNING, "AgentLogin requires an AgentId\n");
1910 if (ast_app_parse_options(agent_login_opts, &opts, NULL, args.options)) {
1911 /* General invalid option syntax. */
1915 /* Find the agent. */
1916 agent = ao2_find(agents, args.agent_id, OBJ_KEY);
1918 ast_verb(3, "Agent '%s' does not exist.\n", args.agent_id);
1919 pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "INVALID");
1923 /* Has someone already logged in as this agent already? */
1925 if (agent->logged) {
1926 agent_unlock(agent);
1927 ast_verb(3, "Agent '%s' already logged in.\n", agent->username);
1928 pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "ALREADY_LOGGED_IN");
1931 agent->logged = ast_channel_ref(chan);
1932 agent->last_disconnect = ast_tvnow();
1933 time(&agent->login_start);
1934 agent_unlock(agent);
1936 agent_login_channel_config(agent, chan);
1938 if (!ast_test_flag(&opts, OPT_SILENT)
1939 && !ast_streamfile(chan, "agent-loginok", ast_channel_language(chan))) {
1940 ast_waitstream(chan, "");
1943 ast_verb(2, "Agent '%s' logged in (format %s/%s)\n", agent->username,
1944 ast_getformatname(ast_channel_readformat(chan)),
1945 ast_getformatname(ast_channel_writeformat(chan)));
1946 send_agent_login(chan, agent->username);
1948 agent_run(agent, chan);
1952 static int agent_function_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
1955 struct agent_pvt *agent;
1956 struct ast_channel *logged;
1957 AST_DECLARE_APP_ARGS(args,
1958 AST_APP_ARG(agentid);
1964 parse = ast_strdupa(data ?: "");
1965 AST_NONSTANDARD_APP_ARGS(args, parse, ':');
1967 if (ast_strlen_zero(args.agentid)) {
1968 ast_log(LOG_WARNING, "The AGENT function requires an argument - agentid!\n");
1972 args.item = "status";
1975 agent = ao2_find(agents, args.agentid, OBJ_KEY);
1977 ast_log(LOG_WARNING, "Agent '%s' not found!\n", args.agentid);
1982 if (!strcasecmp(args.item, "status")) {
1985 if (agent->logged) {
1986 status = "LOGGEDIN";
1988 status = "LOGGEDOUT";
1990 ast_copy_string(buf, status, len);
1991 } else if (!strcasecmp(args.item, "name")) {
1992 ast_copy_string(buf, agent->cfg->full_name, len);
1993 } else if (!strcasecmp(args.item, "mohclass")) {
1994 ast_copy_string(buf, agent->cfg->moh, len);
1995 } else if (!strcasecmp(args.item, "channel")) {
1996 logged = agent_lock_logged(agent);
2000 ast_copy_string(buf, ast_channel_name(logged), len);
2001 ast_channel_unlock(logged);
2002 ast_channel_unref(logged);
2004 pos = strrchr(buf, '-');
2009 } else if (!strcasecmp(args.item, "fullchannel")) {
2010 logged = agent_lock_logged(agent);
2012 ast_copy_string(buf, ast_channel_name(logged), len);
2013 ast_channel_unlock(logged);
2014 ast_channel_unref(logged);
2017 agent_unlock(agent);
2023 static struct ast_custom_function agent_function = {
2025 .read = agent_function_read,
2028 struct agent_complete {
2029 /*! Nth match to return. */
2031 /*! Which match currently on. */
2035 static int complete_agent_search(void *obj, void *arg, void *data, int flags)
2037 struct agent_complete *search = data;
2039 if (++search->which > search->state) {
2045 static char *complete_agent(const char *word, int state)
2048 struct agent_pvt *agent;
2049 struct agent_complete search = {
2053 agent = ao2_callback_data(agents, ast_strlen_zero(word) ? 0 : OBJ_PARTIAL_KEY,
2054 complete_agent_search, (char *) word, &search);
2058 ret = ast_strdup(agent->username);
2063 static int complete_agent_logoff_search(void *obj, void *arg, void *data, int flags)
2065 struct agent_pvt *agent = obj;
2066 struct agent_complete *search = data;
2068 if (!agent->logged) {
2071 if (++search->which > search->state) {
2077 static char *complete_agent_logoff(const char *word, int state)
2080 struct agent_pvt *agent;
2081 struct agent_complete search = {
2085 agent = ao2_callback_data(agents, ast_strlen_zero(word) ? 0 : OBJ_PARTIAL_KEY,
2086 complete_agent_logoff_search, (char *) word, &search);
2090 ret = ast_strdup(agent->username);
2095 static void agent_show_requested(struct ast_cli_args *a, int online_only)
2097 #define FORMAT_HDR "%-8s %-20s %-11s %-30s %s\n"
2098 #define FORMAT_ROW "%-8s %-20s %-11s %-30s %s\n"
2100 struct ao2_iterator iter;
2101 struct agent_pvt *agent;
2102 struct ast_str *out = ast_str_alloca(512);
2103 unsigned int agents_total = 0;
2104 unsigned int agents_logged_in = 0;
2105 unsigned int agents_talking = 0;
2107 ast_cli(a->fd, FORMAT_HDR, "Agent-ID", "Name", "State", "Channel", "Talking with");
2108 iter = ao2_iterator_init(agents, 0);
2109 for (; (agent = ao2_iterator_next(&iter)); ao2_ref(agent, -1)) {
2110 struct ast_channel *logged;
2115 logged = agent_lock_logged(agent);
2117 const char *talking_with;
2121 talking_with = pbx_builtin_getvar_helper(logged, "BRIDGEPEER");
2122 if (!ast_strlen_zero(talking_with)) {
2127 ast_str_set(&out, 0, FORMAT_ROW, agent->username, agent->cfg->full_name,
2128 ast_devstate_str(agent->devstate), ast_channel_name(logged), talking_with);
2129 ast_channel_unlock(logged);
2130 ast_channel_unref(logged);
2132 ast_str_set(&out, 0, FORMAT_ROW, agent->username, agent->cfg->full_name,
2133 ast_devstate_str(agent->devstate), "", "");
2135 agent_unlock(agent);
2137 if (!online_only || logged) {
2138 ast_cli(a->fd, "%s", ast_str_buffer(out));
2141 ao2_iterator_destroy(&iter);
2143 ast_cli(a->fd, "\nDefined agents: %u, Logged in: %u, Talking: %u\n",
2144 agents_total, agents_logged_in, agents_talking);
2150 static char *agent_handle_show_online(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2154 e->command = "agent show online";
2156 "Usage: agent show online\n"
2157 " Provides summary information for logged in agents.\n";
2164 return CLI_SHOWUSAGE;
2167 agent_show_requested(a, 1);
2172 static char *agent_handle_show_all(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2176 e->command = "agent show all";
2178 "Usage: agent show all\n"
2179 " Provides summary information for all agents.\n";
2186 return CLI_SHOWUSAGE;
2189 agent_show_requested(a, 0);
2194 static char *agent_handle_show_specific(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2196 struct agent_pvt *agent;
2197 struct ast_channel *logged;
2198 struct ast_str *out = ast_str_alloca(4096);
2202 e->command = "agent show";
2204 "Usage: agent show <agent-id>\n"
2205 " Show information about the <agent-id> agent\n";
2209 return complete_agent(a->word, a->n);
2215 return CLI_SHOWUSAGE;
2218 agent = ao2_find(agents, a->argv[2], OBJ_KEY);
2220 ast_cli(a->fd, "Agent '%s' not found\n", a->argv[2]);
2225 logged = agent_lock_logged(agent);
2226 ast_str_set(&out, 0, "Id: %s\n", agent->username);
2227 ast_str_append(&out, 0, "Name: %s\n", agent->cfg->full_name);
2228 ast_str_append(&out, 0, "Beep: %s\n", agent->cfg->beep_sound);
2229 ast_str_append(&out, 0, "MOH: %s\n", agent->cfg->moh);
2230 ast_str_append(&out, 0, "RecordCalls: %s\n", AST_CLI_YESNO(agent->cfg->record_agent_calls));
2231 ast_str_append(&out, 0, "State: %s\n", ast_devstate_str(agent->devstate));
2233 const char *talking_with;
2235 ast_str_append(&out, 0, "LoggedInChannel: %s\n", ast_channel_name(logged));
2236 ast_str_append(&out, 0, "LoggedInTime: %ld\n", (long) agent->login_start);
2237 talking_with = pbx_builtin_getvar_helper(logged, "BRIDGEPEER");
2238 if (!ast_strlen_zero(talking_with)) {
2239 ast_str_append(&out, 0, "TalkingWith: %s\n", talking_with);
2240 ast_str_append(&out, 0, "CallStarted: %ld\n", (long) agent->call_start);
2242 ast_channel_unlock(logged);
2243 ast_channel_unref(logged);
2245 agent_unlock(agent);
2248 ast_cli(a->fd, "%s", ast_str_buffer(out));
2253 static char *agent_handle_logoff_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2257 e->command = "agent logoff";
2259 "Usage: agent logoff <agent-id> [soft]\n"
2260 " Sets an agent as no longer logged in.\n"
2261 " If 'soft' is specified, do not hangup existing calls.\n";
2265 return complete_agent_logoff(a->word, a->n);
2266 } else if (a->pos == 3 && a->n == 0
2267 && (ast_strlen_zero(a->word)
2268 || !strncasecmp("soft", a->word, strlen(a->word)))) {
2269 return ast_strdup("soft");
2274 if (a->argc < 3 || 4 < a->argc) {
2275 return CLI_SHOWUSAGE;
2277 if (a->argc == 4 && strcasecmp(a->argv[3], "soft")) {
2278 return CLI_SHOWUSAGE;
2281 if (!agent_logoff_request(a->argv[2], a->argc == 4)) {
2282 ast_cli(a->fd, "Logging out %s\n", a->argv[2]);
2288 static struct ast_cli_entry cli_agents[] = {
2289 AST_CLI_DEFINE(agent_handle_show_online, "Show status of online agents"),
2290 AST_CLI_DEFINE(agent_handle_show_all, "Show status of all agents"),
2291 AST_CLI_DEFINE(agent_handle_show_specific, "Show information about an agent"),
2292 AST_CLI_DEFINE(agent_handle_logoff_cmd, "Sets an agent offline"),
2295 static int action_agents(struct mansession *s, const struct message *m)
2297 const char *id = astman_get_header(m, "ActionID");
2298 char id_text[AST_MAX_BUF];
2299 struct ao2_iterator iter;
2300 struct agent_pvt *agent;
2301 struct ast_str *out = ast_str_alloca(4096);
2303 if (!ast_strlen_zero(id)) {
2304 snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", id);
2308 astman_send_ack(s, m, "Agents will follow");
2310 iter = ao2_iterator_init(agents, 0);
2311 for (; (agent = ao2_iterator_next(&iter)); ao2_ref(agent, -1)) {
2312 struct ast_party_id party_id;
2313 struct ast_channel *logged;
2314 const char *login_chan;
2315 const char *talking_to;
2316 const char *talking_to_chan;
2321 logged = agent_lock_logged(agent);
2325 * AGENT_LOGGEDOFF - Agent isn't logged in
2326 * AGENT_IDLE - Agent is logged in, and waiting for call
2327 * AGENT_ONCALL - Agent is logged in, and on a call
2328 * AGENT_UNKNOWN - Don't know anything about agent. Shouldn't ever get this.
2332 login_chan = ast_channel_name(logged);
2333 login_start = agent->login_start;
2334 talking_to_chan = pbx_builtin_getvar_helper(logged, "BRIDGEPEER");
2335 if (!ast_strlen_zero(talking_to_chan)) {
2336 party_id = ast_channel_connected_effective_id(logged);
2337 talking_to = S_COR(party_id.number.valid, party_id.number.str, "n/a");
2338 status = "AGENT_ONCALL";
2341 talking_to_chan = "n/a";
2342 status = "AGENT_IDLE";
2348 talking_to_chan = "n/a";
2349 status = "AGENT_LOGGEDOFF";
2352 ast_str_set(&out, 0, "Agent: %s\r\n", agent->username);
2353 ast_str_append(&out, 0, "Name: %s\r\n", S_OR(agent->cfg->full_name, "None"));
2354 ast_str_append(&out, 0, "Status: %s\r\n", status);
2355 ast_str_append(&out, 0, "LoggedInChan: %s\r\n", login_chan);
2356 ast_str_append(&out, 0, "LoggedInTime: %ld\r\n", (long) login_start);
2357 ast_str_append(&out, 0, "TalkingTo: %s\r\n", talking_to);
2358 ast_str_append(&out, 0, "TalkingToChan: %s\r\n", talking_to_chan);
2361 ast_channel_unlock(logged);
2362 ast_channel_unref(logged);
2364 agent_unlock(agent);
2366 astman_append(s, "Event: Agents\r\n"
2368 ast_str_buffer(out), id_text);
2370 ao2_iterator_destroy(&iter);
2372 astman_append(s, "Event: AgentsComplete\r\n"
2378 static int action_agent_logoff(struct mansession *s, const struct message *m)
2380 const char *agent = astman_get_header(m, "Agent");
2381 const char *soft_s = astman_get_header(m, "Soft"); /* "true" is don't hangup */
2383 if (ast_strlen_zero(agent)) {
2384 astman_send_error(s, m, "No agent specified");
2388 if (!agent_logoff_request(agent, ast_true(soft_s))) {
2389 astman_send_ack(s, m, "Agent logged out");
2391 astman_send_error(s, m, "No such agent");
2397 static int unload_module(void)
2399 struct ast_bridge *holding;
2401 /* Unregister dialplan applications */
2402 ast_unregister_application(app_agent_login);
2403 ast_unregister_application(app_agent_request);
2405 /* Unregister dialplan functions */
2406 ast_custom_function_unregister(&agent_function);
2408 /* Unregister manager command */
2409 ast_manager_unregister("Agents");
2410 ast_manager_unregister("AgentLogoff");
2412 /* Unregister CLI commands */
2413 ast_cli_unregister_multiple(cli_agents, ARRAY_LEN(cli_agents));
2415 ast_devstate_prov_del("Agent");
2417 /* Destroy agent holding bridge. */
2418 holding = ao2_global_obj_replace(agent_holding, NULL);
2420 ast_bridge_destroy(holding);
2424 ao2_ref(agents, -1);
2429 static int load_module(void)
2433 agents = ao2_container_alloc_rbtree(AO2_ALLOC_OPT_LOCK_MUTEX,
2434 AO2_CONTAINER_ALLOC_OPT_DUPS_REPLACE, agent_pvt_sort_cmp, agent_pvt_cmp);
2436 return AST_MODULE_LOAD_FAILURE;
2438 if (load_config()) {
2439 ast_log(LOG_ERROR, "Unable to load config. Not loading module.\n");
2440 ao2_ref(agents, -1);
2442 return AST_MODULE_LOAD_DECLINE;
2445 /* Init agent holding bridge v_table. */
2446 bridging_init_agent_hold();
2448 /* Setup to provide Agent:agent-id device state. */
2449 res |= ast_devstate_prov_add("Agent", agent_pvt_devstate_get);
2452 res |= ast_cli_register_multiple(cli_agents, ARRAY_LEN(cli_agents));
2454 /* Manager commands */
2455 res |= ast_manager_register_xml("Agents", EVENT_FLAG_AGENT, action_agents);
2456 res |= ast_manager_register_xml("AgentLogoff", EVENT_FLAG_AGENT, action_agent_logoff);
2458 /* Dialplan Functions */
2459 res |= ast_custom_function_register(&agent_function);
2461 /* Dialplan applications */
2462 res |= ast_register_application_xml(app_agent_login, agent_login_exec);
2463 res |= ast_register_application_xml(app_agent_request, agent_request_exec);
2467 return AST_MODULE_LOAD_FAILURE;
2469 return AST_MODULE_LOAD_SUCCESS;
2472 static int reload(void)
2474 if (aco_process_config(&cfg_info, 1) == ACO_PROCESS_ERROR) {
2475 /* Just keep the config we already have in place. */
2481 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Call center agent pool applications",
2482 .load = load_module,
2483 .unload = unload_module,
2485 .load_pri = AST_MODPRI_DEVSTATE_PROVIDER,