Asterisk - The Open Source Telephony Project GIT-master-0deac78
app_agent_pool.c
Go to the documentation of this file.
1/*
2 * Asterisk -- An open source telephony toolkit.
3 *
4 * Copyright (C) 2013 Digium, Inc.
5 *
6 * Richard Mudgett <rmudgett@digium.com>
7 *
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.
13 *
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.
17 */
18
19/*!
20 * \file
21 * \brief Call center agent pool.
22 *
23 * \author Richard Mudgett <rmudgett@digium.com>
24 *
25 * See Also:
26 * \arg \ref AstCREDITS
27 * \arg \ref agents.conf "Config_agent"
28 */
29
30/*!
31 * \page agents.conf agents.conf
32 * \verbinclude agents.conf.sample
33 */
34
35/*** MODULEINFO
36 <support_level>core</support_level>
37 ***/
38
39
40#include "asterisk.h"
41
42#include "asterisk/cli.h"
43#include "asterisk/app.h"
44#include "asterisk/pbx.h"
45#include "asterisk/module.h"
46#include "asterisk/channel.h"
47#include "asterisk/bridge.h"
53#include "asterisk/astobj2.h"
56#include "asterisk/causes.h"
57
58/*** DOCUMENTATION
59 <application name="AgentLogin" language="en_US">
60 <since>
61 <version>12.0.0</version>
62 </since>
63 <synopsis>
64 Login an agent.
65 </synopsis>
66 <syntax argsep=",">
67 <parameter name="AgentId" required="true" />
68 <parameter name="options">
69 <optionlist>
70 <option name="s">
71 <para>silent login - do not announce the login ok segment after
72 agent logged on.</para>
73 </option>
74 </optionlist>
75 </parameter>
76 </syntax>
77 <description>
78 <para>Login an agent to the system. Any agent authentication is assumed to
79 already be done by dialplan. While logged in, the agent can receive calls
80 and will hear the sound file specified by the config option custom_beep
81 when a new call comes in for the agent. Login failures will continue in
82 the dialplan with <variable>AGENT_STATUS</variable> set.</para>
83 <para>Before logging in, you can setup on the real agent channel the
84 CHANNEL(dtmf_features) an agent will have when talking to a caller
85 and you can setup on the channel running this application the
86 CONNECTEDLINE() information the agent will see while waiting for a
87 caller.</para>
88 <para><variable>AGENT_STATUS</variable> enumeration values:</para>
89 <enumlist>
90 <enum name = "INVALID"><para>The specified agent is invalid.</para></enum>
91 <enum name = "ALREADY_LOGGED_IN"><para>The agent is already logged in.</para></enum>
92 </enumlist>
93 <note><para>The Agent:<replaceable>AgentId</replaceable> device state is
94 available to monitor the status of the agent.</para></note>
95 </description>
96 <see-also>
97 <ref type="application">Authenticate</ref>
98 <ref type="application">Queue</ref>
99 <ref type="application">AddQueueMember</ref>
100 <ref type="application">RemoveQueueMember</ref>
101 <ref type="application">PauseQueueMember</ref>
102 <ref type="application">UnpauseQueueMember</ref>
103 <ref type="function">AGENT</ref>
104 <ref type="function">CHANNEL</ref>
105 <ref type="function">CONNECTEDLINE</ref>
106 <ref type="filename">agents.conf</ref>
107 <ref type="filename">queues.conf</ref>
108 </see-also>
109 </application>
110 <application name="AgentRequest" language="en_US">
111 <since>
112 <version>12.0.0</version>
113 </since>
114 <synopsis>
115 Request an agent to connect with the channel.
116 </synopsis>
117 <syntax argsep=",">
118 <parameter name="AgentId" required="true" />
119 </syntax>
120 <description>
121 <para>Request an agent to connect with the channel. Failure to find,
122 alert the agent, or acknowledge the call will continue in the dialplan
123 with <variable>AGENT_STATUS</variable> set.</para>
124 <para><variable>AGENT_STATUS</variable> enumeration values:</para>
125 <enumlist>
126 <enum name = "INVALID"><para>The specified agent is invalid.</para></enum>
127 <enum name = "NOT_LOGGED_IN"><para>The agent is not available.</para></enum>
128 <enum name = "BUSY"><para>The agent is on another call.</para></enum>
129 <enum name = "NOT_CONNECTED"><para>The agent did not connect with the
130 call. The agent most likely did not acknowledge the call.</para></enum>
131 <enum name = "ERROR"><para>Alerting the agent failed.</para></enum>
132 </enumlist>
133 </description>
134 <see-also>
135 <ref type="application">AgentLogin</ref>
136 </see-also>
137 </application>
138 <function name="AGENT" language="en_US">
139 <since>
140 <version>12.0.0</version>
141 </since>
142 <synopsis>
143 Gets information about an Agent
144 </synopsis>
145 <syntax argsep=":">
146 <parameter name="AgentId" required="true" />
147 <parameter name="item">
148 <para>The valid items to retrieve are:</para>
149 <enumlist>
150 <enum name="status">
151 <para>(default) The status of the agent (LOGGEDIN | LOGGEDOUT)</para>
152 </enum>
153 <enum name="name">
154 <para>The name of the agent</para>
155 </enum>
156 <enum name="mohclass">
157 <para>MusicOnHold class</para>
158 </enum>
159 <enum name="channel">
160 <para>The name of the active channel for the Agent (AgentLogin)</para>
161 </enum>
162 <enum name="fullchannel">
163 <para>The untruncated name of the active channel for the Agent (AgentLogin)</para>
164 </enum>
165 </enumlist>
166 </parameter>
167 </syntax>
168 <description></description>
169 </function>
170 <manager name="Agents" language="en_US">
171 <since>
172 <version>1.2.0</version>
173 </since>
174 <synopsis>
175 Lists agents and their status.
176 </synopsis>
177 <syntax>
178 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
179 </syntax>
180 <description>
181 <para>Will list info about all defined agents.</para>
182 </description>
183 <see-also>
184 <ref type="managerEvent">Agents</ref>
185 <ref type="managerEvent">AgentsComplete</ref>
186 </see-also>
187 </manager>
188 <managerEvent language="en_US" name="Agents">
189 <managerEventInstance class="EVENT_FLAG_AGENT">
190 <since>
191 <version>12.0.0</version>
192 </since>
193 <synopsis>
194 Response event in a series to the Agents AMI action containing
195 information about a defined agent.
196 </synopsis>
197 <syntax>
198 <parameter name="Agent">
199 <para>Agent ID of the agent.</para>
200 </parameter>
201 <parameter name="Name">
202 <para>User friendly name of the agent.</para>
203 </parameter>
204 <parameter name="Status">
205 <para>Current status of the agent.</para>
206 <para>The valid values are:</para>
207 <enumlist>
208 <enum name="AGENT_LOGGEDOFF" />
209 <enum name="AGENT_IDLE" />
210 <enum name="AGENT_ONCALL" />
211 </enumlist>
212 </parameter>
213 <parameter name="TalkingToChan">
214 <para><variable>BRIDGEPEER</variable> value on agent channel.</para>
215 <para>Present if Status value is <literal>AGENT_ONCALL</literal>.</para>
216 </parameter>
217 <parameter name="CallStarted">
218 <para>Epoch time when the agent started talking with the caller.</para>
219 <para>Present if Status value is <literal>AGENT_ONCALL</literal>.</para>
220 </parameter>
221 <parameter name="LoggedInTime">
222 <para>Epoch time when the agent logged in.</para>
223 <para>Present if Status value is <literal>AGENT_IDLE</literal> or <literal>AGENT_ONCALL</literal>.</para>
224 </parameter>
225 <channel_snapshot/>
226 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
227 </syntax>
228 <description>
229 <para>The channel snapshot is present if the Status value is <literal>AGENT_IDLE</literal> or <literal>AGENT_ONCALL</literal>.</para>
230 </description>
231 <see-also>
232 <ref type="manager">Agents</ref>
233 </see-also>
234 </managerEventInstance>
235 </managerEvent>
236 <managerEvent language="en_US" name="AgentsComplete">
237 <managerEventInstance class="EVENT_FLAG_AGENT">
238 <since>
239 <version>12.0.0</version>
240 </since>
241 <synopsis>
242 Final response event in a series of events to the Agents AMI action.
243 </synopsis>
244 <syntax>
245 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
246 </syntax>
247 <see-also>
248 <ref type="manager">Agents</ref>
249 </see-also>
250 </managerEventInstance>
251 </managerEvent>
252 <manager name="AgentLogoff" language="en_US">
253 <since>
254 <version>1.2.0</version>
255 </since>
256 <synopsis>
257 Sets an agent as no longer logged in.
258 </synopsis>
259 <syntax>
260 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
261 <parameter name="Agent" required="true">
262 <para>Agent ID of the agent to log off.</para>
263 </parameter>
264 <parameter name="Soft">
265 <para>Set to <literal>true</literal> to not hangup existing calls.</para>
266 </parameter>
267 </syntax>
268 <description>
269 <para>Sets an agent as no longer logged in.</para>
270 </description>
271 </manager>
272 <configInfo name="app_agent_pool" language="en_US">
273 <synopsis>Agent pool applications</synopsis>
274 <description>
275 <note><para>Option changes take effect on agent login or after an agent
276 disconnects from a call.</para></note>
277 </description>
278 <configFile name="agents.conf">
279 <configObject name="global">
280 <synopsis>Unused, but reserved.</synopsis>
281 </configObject>
282 <configObject name="agent-id">
283 <since>
284 <version>12.0.0</version>
285 </since>
286 <synopsis>Configure an agent for the pool.</synopsis>
287 <description>
288 <xi:include xpointer="xpointer(/docs/configInfo[@name='app_agent_pool']/description/note)" />
289 </description>
290 <configOption name="ackcall">
291 <since>
292 <version>12.0.0</version>
293 </since>
294 <synopsis>Enable to require the agent to acknowledge a call.</synopsis>
295 <description>
296 <para>Enable to require the agent to give a DTMF acknowledgement
297 when the agent receives a call.</para>
298 <note><para>The option is overridden by <variable>AGENTACKCALL</variable> on agent login.</para></note>
299 <xi:include xpointer="xpointer(/docs/configInfo[@name='app_agent_pool']/description/note)" />
300 </description>
301 </configOption>
302 <configOption name="acceptdtmf">
303 <since>
304 <version>12.0.0</version>
305 </since>
306 <synopsis>DTMF key sequence the agent uses to acknowledge a call.</synopsis>
307 <description>
308 <note><para>The option is overridden by <variable>AGENTACCEPTDTMF</variable> on agent login.</para></note>
309 <note><para>The option is ignored unless the ackcall option is enabled.</para></note>
310 <xi:include xpointer="xpointer(/docs/configInfo[@name='app_agent_pool']/description/note)" />
311 </description>
312 </configOption>
313 <configOption name="autologoff">
314 <since>
315 <version>12.0.0</version>
316 </since>
317 <synopsis>Time the agent has to acknowledge a call before being logged off.</synopsis>
318 <description>
319 <para>Set how many seconds a call for the agent has to wait for the
320 agent to acknowledge the call before the agent is automatically
321 logged off. If set to zero then the call will wait forever for
322 the agent to acknowledge.</para>
323 <note><para>The option is overridden by <variable>AGENTAUTOLOGOFF</variable> on agent login.</para></note>
324 <note><para>The option is ignored unless the ackcall option is enabled.</para></note>
325 <xi:include xpointer="xpointer(/docs/configInfo[@name='app_agent_pool']/description/note)" />
326 </description>
327 </configOption>
328 <configOption name="wrapuptime">
329 <since>
330 <version>12.0.0</version>
331 </since>
332 <synopsis>Minimum time the agent has between calls.</synopsis>
333 <description>
334 <para>Set the minimum amount of time in milliseconds after
335 disconnecting a call before the agent can receive a new call.</para>
336 <note><para>The option is overridden by <variable>AGENTWRAPUPTIME</variable> on agent login.</para></note>
337 <xi:include xpointer="xpointer(/docs/configInfo[@name='app_agent_pool']/description/note)" />
338 </description>
339 </configOption>
340 <configOption name="musiconhold">
341 <since>
342 <version>12.0.0</version>
343 </since>
344 <synopsis>Music on hold class the agent listens to between calls.</synopsis>
345 <description>
346 <xi:include xpointer="xpointer(/docs/configInfo[@name='app_agent_pool']/description/note)" />
347 </description>
348 </configOption>
349 <configOption name="recordagentcalls">
350 <since>
351 <version>12.0.0</version>
352 </since>
353 <synopsis>Enable to automatically record calls the agent takes.</synopsis>
354 <description>
355 <para>Enable recording calls the agent takes automatically by
356 invoking the automixmon DTMF feature when the agent connects
357 to a caller. See <filename>features.conf.sample</filename> for information about
358 the automixmon feature.</para>
359 <xi:include xpointer="xpointer(/docs/configInfo[@name='app_agent_pool']/description/note)" />
360 </description>
361 </configOption>
362 <configOption name="custom_beep">
363 <since>
364 <version>12.4.0</version>
365 </since>
366 <synopsis>Sound file played to alert the agent when a call is present.</synopsis>
367 <description>
368 <xi:include xpointer="xpointer(/docs/configInfo[@name='app_agent_pool']/description/note)" />
369 </description>
370 </configOption>
371 <configOption name="fullname">
372 <since>
373 <version>12.0.0</version>
374 </since>
375 <synopsis>A friendly name for the agent used in log messages.</synopsis>
376 <description>
377 <xi:include xpointer="xpointer(/docs/configInfo[@name='app_agent_pool']/description/note)" />
378 </description>
379 </configOption>
380 </configObject>
381 </configFile>
382 </configInfo>
383 ***/
384
385/* ------------------------------------------------------------------- */
386
387#define AST_MAX_BUF 256
388
389/*! Maximum wait time (in ms) for the custom_beep file to play announcing the caller. */
390#define CALLER_SAFETY_TIMEOUT_TIME (2 * 60 * 1000)
391
392/*! Number of seconds to wait for local channel optimizations to complete. */
393#define LOGIN_WAIT_TIMEOUT_TIME 5
394
395static const char app_agent_login[] = "AgentLogin";
396static const char app_agent_request[] = "AgentRequest";
397
398/*! Agent config parameters. */
399struct agent_cfg {
401 /*! Identification of the agent. (agents config container key) */
403 /*! Name of agent for logging and querying purposes */
405
406 /*!
407 * \brief DTMF string for an agent to accept a call.
408 *
409 * \note The channel variable AGENTACCEPTDTMF overrides on login.
410 */
412 /*! Beep sound file to use. Alert the agent a call is waiting. */
414 /*! MOH class to use while agent waiting for call. */
416 );
417 /*!
418 * \brief Number of seconds for agent to ack a call before being logged off.
419 *
420 * \note The channel variable AGENTAUTOLOGOFF overrides on login.
421 * \note If zero then timer is disabled.
422 */
423 unsigned int auto_logoff;
424 /*!
425 * \brief Time after a call in ms before the agent can get a new call.
426 *
427 * \note The channel variable AGENTWRAPUPTIME overrides on login.
428 */
429 unsigned int wrapup_time;
430 /*!
431 * \brief TRUE if agent needs to ack a call to accept it.
432 *
433 * \note The channel variable AGENTACKCALL overrides on login.
434 */
436 /*! TRUE if agent calls are automatically recorded. */
438};
439
440/*!
441 * \internal
442 * \brief Agent config ao2 container sort function.
443 * \since 12.0.0
444 *
445 * \param obj_left pointer to the (user-defined part) of an object.
446 * \param obj_right pointer to the (user-defined part) of an object.
447 * \param flags flags from ao2_callback()
448 * OBJ_POINTER - if set, 'obj_right', is an object.
449 * OBJ_KEY - if set, 'obj_right', is a search key item that is not an object.
450 * OBJ_PARTIAL_KEY - if set, 'obj_right', is a partial search key item that is not an object.
451 *
452 * \retval <0 if obj_left < obj_right
453 * \retval =0 if obj_left == obj_right
454 * \retval >0 if obj_left > obj_right
455 */
456static int agent_cfg_sort_cmp(const void *obj_left, const void *obj_right, int flags)
457{
458 const struct agent_cfg *cfg_left = obj_left;
459 const struct agent_cfg *cfg_right = obj_right;
460 const char *right_key = obj_right;
461 int cmp;
462
463 switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
464 default:
465 case OBJ_POINTER:
466 right_key = cfg_right->username;
467 /* Fall through */
468 case OBJ_KEY:
469 cmp = strcmp(cfg_left->username, right_key);
470 break;
471 case OBJ_PARTIAL_KEY:
472 cmp = strncmp(cfg_left->username, right_key, strlen(right_key));
473 break;
474 }
475 return cmp;
476}
477
478static void agent_cfg_destructor(void *vdoomed)
479{
480 struct agent_cfg *doomed = vdoomed;
481
483}
484
485static void *agent_cfg_alloc(const char *name)
486{
487 struct agent_cfg *cfg;
488
489 cfg = ao2_alloc_options(sizeof(*cfg), agent_cfg_destructor,
491 if (!cfg || ast_string_field_init(cfg, 64)) {
492 ao2_cleanup(cfg);
493 return NULL;
494 }
496 return cfg;
497}
498
499static void *agent_cfg_find(struct ao2_container *agents, const char *username)
500{
502}
503
504/*! Agents configuration */
506 /*! Master configured agents container. */
508};
509
510static struct aco_type agent_type = {
511 .type = ACO_ITEM,
512 .name = "agent-id",
513 .category_match = ACO_BLACKLIST_EXACT,
514 .category = "general",
515 .item_alloc = agent_cfg_alloc,
516 .item_find = agent_cfg_find,
517 .item_offset = offsetof(struct agents_cfg, agents),
518};
519
521
522/* The general category is reserved, but unused */
523static struct aco_type general_type = {
524 .type = ACO_GLOBAL,
525 .name = "global",
526 .category_match = ACO_WHITELIST_EXACT,
527 .category = "general",
528};
529
530static struct aco_file agents_conf = {
531 .filename = "agents.conf",
532 .types = ACO_TYPES(&general_type, &agent_type),
533};
534
536
537static void agents_cfg_destructor(void *vdoomed)
538{
539 struct agents_cfg *doomed = vdoomed;
540
541 ao2_cleanup(doomed->agents);
542 doomed->agents = NULL;
543}
544
545/*!
546 * \internal
547 * \brief Create struct agents_cfg object.
548 * \since 12.0.0
549 *
550 * \note A lock is not needed for the object or any secondary
551 * created cfg objects. These objects are immutable after the
552 * config is loaded and applied.
553 *
554 * \retval New struct agents_cfg object.
555 * \retval NULL on error.
556 */
557static void *agents_cfg_alloc(void)
558{
559 struct agents_cfg *cfg;
560
561 cfg = ao2_alloc_options(sizeof(*cfg), agents_cfg_destructor,
563 if (!cfg) {
564 return NULL;
565 }
568 if (!cfg->agents) {
569 ao2_ref(cfg, -1);
570 cfg = NULL;
571 }
572 return cfg;
573}
574
575static void agents_post_apply_config(void);
576
578 .files = ACO_FILES(&agents_conf),
579 .post_apply_config = agents_post_apply_config,
580);
581
582static void destroy_config(void)
583{
585 aco_info_destroy(&cfg_info);
586}
587
588static int load_config(void)
589{
590 if (aco_info_init(&cfg_info)) {
591 return -1;
592 }
593
594 /* Agent options */
595 aco_option_register(&cfg_info, "ackcall", ACO_EXACT, agent_types, "no", OPT_BOOL_T, 1, FLDSET(struct agent_cfg, ack_call));
596 aco_option_register(&cfg_info, "acceptdtmf", ACO_EXACT, agent_types, "#", OPT_STRINGFIELD_T, 1, STRFLDSET(struct agent_cfg, dtmf_accept));
597 aco_option_register(&cfg_info, "autologoff", ACO_EXACT, agent_types, "0", OPT_UINT_T, 0, FLDSET(struct agent_cfg, auto_logoff));
598 aco_option_register(&cfg_info, "wrapuptime", ACO_EXACT, agent_types, "0", OPT_UINT_T, 0, FLDSET(struct agent_cfg, wrapup_time));
599 aco_option_register(&cfg_info, "musiconhold", ACO_EXACT, agent_types, "default", OPT_STRINGFIELD_T, 0, STRFLDSET(struct agent_cfg, moh));
600 aco_option_register(&cfg_info, "recordagentcalls", ACO_EXACT, agent_types, "no", OPT_BOOL_T, 1, FLDSET(struct agent_cfg, record_agent_calls));
601 aco_option_register(&cfg_info, "custom_beep", ACO_EXACT, agent_types, "beep", OPT_STRINGFIELD_T, 0, STRFLDSET(struct agent_cfg, beep_sound));
602 aco_option_register(&cfg_info, "fullname", ACO_EXACT, agent_types, NULL, OPT_STRINGFIELD_T, 0, STRFLDSET(struct agent_cfg, full_name));
603
604 if (aco_process_config(&cfg_info, 0) == ACO_PROCESS_ERROR) {
605 return -1;
606 }
607
608 return 0;
609}
610
612 /*! The agent is defined but an agent is not present. */
614 /*! Forced initial login wait to allow any local channel optimizations to happen. */
616 /*! The agent is ready for a call. */
618 /*! The agent has a call waiting to connect. */
620 /*! The agent needs to ack the call. */
622 /*! The agent is connected with a call. */
624 /*! The agent is resting between calls. */
626 /*! The agent is being kicked out. */
628};
629
630/*! Agent config option override flags. */
636};
637
638/*! \brief Structure representing an agent. */
639struct agent_pvt {
641 /*! Identification of the agent. (agents container key) */
643 /*! Login override DTMF string for an agent to accept a call. */
645 );
646 /*! Connected line information to send when reentering the holding bridge. */
648 /*! Flags show if settings were overridden by channel vars. */
649 unsigned int flags;
650 /*! Login override number of seconds for agent to ack a call before being logged off. */
652 /*! Login override time after a call in ms before the agent can get a new call. */
654 /*! Login override if agent needs to ack a call to accept it. */
655 unsigned int override_ack_call:1;
656
657 /*! TRUE if the agent is requested to logoff when the current call ends. */
658 unsigned int deferred_logoff:1;
659
660 /*! Mark and sweep config update to determine if an agent is dead. */
661 unsigned int the_mark:1;
662 /*!
663 * \brief TRUE if the agent is no longer configured and is being destroyed.
664 *
665 * \note Agents cannot log in if they are dead.
666 */
667 unsigned int dead:1;
668
669 /*! Agent control state variable. */
671 /*! Custom device state of agent. */
673
674 /*! When agent first logged in */
676 /*! When agent login probation started. */
678 /*! When call started */
680 /*! When ack timer started */
681 struct timeval ack_time;
682 /*! When last disconnected */
683 struct timeval last_disconnect;
684
685 /*! Caller is waiting in this bridge for agent to join. (Holds ref) */
687 /*! Agent is logged in with this channel. (Holds ref) (NULL if not logged in.) */
689 /*! Active config values from config file. (Holds ref) */
690 struct agent_cfg *cfg;
691};
692
693/*! Container of defined agents. */
694static struct ao2_container *agents;
695
696/*!
697 * \brief Lock the agent.
698 *
699 * \param agent Agent to lock
700 */
701#define agent_lock(agent) _agent_lock(agent, __FILE__, __PRETTY_FUNCTION__, __LINE__, #agent)
702static inline void _agent_lock(struct agent_pvt *agent, const char *file, const char *function, int line, const char *var)
703{
704 __ao2_lock(agent, AO2_LOCK_REQ_MUTEX, file, function, line, var);
705}
706
707/*!
708 * \brief Unlock the agent.
709 *
710 * \param agent Agent to unlock
711 */
712#define agent_unlock(agent) _agent_unlock(agent, __FILE__, __PRETTY_FUNCTION__, __LINE__, #agent)
713static inline void _agent_unlock(struct agent_pvt *agent, const char *file, const char *function, int line, const char *var)
714{
715 __ao2_unlock(agent, file, function, line, var);
716}
717
718/*!
719 * \internal
720 * \brief Obtain the agent logged in channel lock if it exists.
721 * \since 12.0.0
722 *
723 * \param agent Pointer to the LOCKED agent_pvt.
724 *
725 * \note Assumes the agent lock is already obtained.
726 *
727 * \note Defined locking order is channel lock then agent lock.
728 */
729static struct ast_channel *agent_lock_logged(struct agent_pvt *agent)
730{
731 struct ast_channel *logged;
732
733 for (;;) {
734 if (!agent->logged) { /* No owner. Nothing to do. */
735 return NULL;
736 }
737
738 /* If we don't ref the logged, it could be killed when we unlock the agent. */
739 logged = ast_channel_ref(agent->logged);
740
741 /* Locking logged requires us to lock channel, then agent. */
742 agent_unlock(agent);
743 ast_channel_lock(logged);
744 agent_lock(agent);
745
746 /* Check if logged changed during agent unlock period */
747 if (logged != agent->logged) {
748 /* Channel changed. Unref and do another pass. */
749 ast_channel_unlock(logged);
750 ast_channel_unref(logged);
751 } else {
752 /* Channel stayed the same. Return it. */
753 return logged;
754 }
755 }
756}
757
758/*!
759 * \internal
760 * \brief Get the Agent:agent_id device state.
761 * \since 12.0.0
762 *
763 * \param agent_id Username of the agent.
764 *
765 * \details
766 * Search the agents container for the agent and return the
767 * current state.
768 *
769 * \return Device state of the agent.
770 */
771static enum ast_device_state agent_pvt_devstate_get(const char *agent_id)
772{
773 enum ast_device_state dev_state = AST_DEVICE_INVALID;
774 struct agent_pvt *agent;
775
776 agent = ao2_find(agents, agent_id, OBJ_KEY);
777 if (agent) {
778 agent_lock(agent);
779 dev_state = agent->devstate;
780 agent_unlock(agent);
781 ao2_ref(agent, -1);
782 }
783 return dev_state;
784}
785
786/*!
787 * \internal
788 * \brief Request an agent device state be updated.
789 * \since 12.0.0
790 *
791 * \param agent_id Which agent needs the device state updated.
792 */
793static void agent_devstate_changed(const char *agent_id)
794{
796}
797
798static void agent_pvt_destructor(void *vdoomed)
799{
800 struct agent_pvt *doomed = vdoomed;
801
802 /* Make sure device state reflects agent destruction. */
803 if (!ast_strlen_zero(doomed->username)) {
804 ast_debug(1, "Agent %s: Destroyed.\n", doomed->username);
806 }
807
809 if (doomed->caller_bridge) {
811 doomed->caller_bridge = NULL;
812 }
813 if (doomed->logged) {
814 doomed->logged = ast_channel_unref(doomed->logged);
815 }
816 ao2_cleanup(doomed->cfg);
817 doomed->cfg = NULL;
819}
820
821static struct agent_pvt *agent_pvt_new(struct agent_cfg *cfg)
822{
823 struct agent_pvt *agent;
824
825 agent = ao2_alloc(sizeof(*agent), agent_pvt_destructor);
826 if (!agent) {
827 return NULL;
828 }
829 if (ast_string_field_init(agent, 32)) {
830 ao2_ref(agent, -1);
831 return NULL;
832 }
835 ao2_ref(cfg, +1);
836 agent->cfg = cfg;
838 return agent;
839}
840
841/*!
842 * \internal
843 * \brief Agents ao2 container sort function.
844 * \since 12.0.0
845 *
846 * \param obj_left pointer to the (user-defined part) of an object.
847 * \param obj_right pointer to the (user-defined part) of an object.
848 * \param flags flags from ao2_callback()
849 * OBJ_POINTER - if set, 'obj_right', is an object.
850 * OBJ_KEY - if set, 'obj_right', is a search key item that is not an object.
851 * OBJ_PARTIAL_KEY - if set, 'obj_right', is a partial search key item that is not an object.
852 *
853 * \retval <0 if obj_left < obj_right
854 * \retval =0 if obj_left == obj_right
855 * \retval >0 if obj_left > obj_right
856 */
857static int agent_pvt_sort_cmp(const void *obj_left, const void *obj_right, int flags)
858{
859 const struct agent_pvt *agent_left = obj_left;
860 const struct agent_pvt *agent_right = obj_right;
861 const char *right_key = obj_right;
862 int cmp;
863
864 switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
865 default:
866 case OBJ_POINTER:
867 right_key = agent_right->username;
868 /* Fall through */
869 case OBJ_KEY:
870 cmp = strcmp(agent_left->username, right_key);
871 break;
872 case OBJ_PARTIAL_KEY:
873 cmp = strncmp(agent_left->username, right_key, strlen(right_key));
874 break;
875 }
876 return cmp;
877}
878
879/*!
880 * \internal
881 * \brief ao2_find() callback function.
882 * \since 12.0.0
883 *
884 * Usage:
885 * found = ao2_find(agents, agent, OBJ_POINTER);
886 * found = ao2_find(agents, "agent-id", OBJ_KEY);
887 * found = ao2_find(agents, agent->logged, 0);
888 */
889static int agent_pvt_cmp(void *obj, void *arg, int flags)
890{
891 const struct agent_pvt *agent = obj;
892 int cmp;
893
894 switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
895 case OBJ_POINTER:
896 case OBJ_KEY:
897 case OBJ_PARTIAL_KEY:
898 cmp = CMP_MATCH;
899 break;
900 default:
901 if (agent->logged == arg) {
902 cmp = CMP_MATCH;
903 } else {
904 cmp = 0;
905 }
906 break;
907 }
908 return cmp;
909}
910
911static int agent_mark(void *obj, void *arg, int flags)
912{
913 struct agent_pvt *agent = obj;
914
915 agent_lock(agent);
916 agent->the_mark = 1;
917 agent_unlock(agent);
918 return 0;
919}
920
921static void agents_mark(void)
922{
924}
925
926static int agent_sweep(void *obj, void *arg, int flags)
927{
928 struct agent_pvt *agent = obj;
929 int cmp = 0;
930
931 agent_lock(agent);
932 if (agent->the_mark) {
933 agent->the_mark = 0;
934 agent->dead = 1;
935 /* Unlink dead agents immediately. */
936 cmp = CMP_MATCH;
937 }
938 agent_unlock(agent);
939 return cmp;
940}
941
942static void agents_sweep(void)
943{
944 struct ao2_iterator *iter;
945 struct agent_pvt *agent;
946 struct ast_channel *logged;
947
949 if (!iter) {
950 return;
951 }
952 for (; (agent = ao2_iterator_next(iter)); ao2_ref(agent, -1)) {
953 agent_lock(agent);
954 if (agent->logged) {
955 logged = ast_channel_ref(agent->logged);
956 } else {
957 logged = NULL;
958 }
959 agent_unlock(agent);
960 if (!logged) {
961 continue;
962 }
964 "Forced logoff of agent %s(%s). Agent no longer configured.\n",
965 agent->username, ast_channel_name(logged));
967 ast_channel_unref(logged);
968 }
970}
971
973{
974 struct ao2_iterator iter;
975 struct agent_cfg *cfg;
977
978 ast_assert(cfgs != NULL);
979
980 agents_mark();
981 iter = ao2_iterator_init(cfgs->agents, 0);
982 for (; (cfg = ao2_iterator_next(&iter)); ao2_ref(cfg, -1)) {
983 RAII_VAR(struct agent_pvt *, agent, ao2_find(agents, cfg->username, OBJ_KEY), ao2_cleanup);
984
985 if (agent) {
986 agent_lock(agent);
987 agent->the_mark = 0;
988 if (!agent->logged) {
989 struct agent_cfg *cfg_old;
990
991 /* Replace the config of agents not logged in. */
992 cfg_old = agent->cfg;
993 ao2_ref(cfg, +1);
994 agent->cfg = cfg;
995 ao2_cleanup(cfg_old);
996 }
997 agent_unlock(agent);
998 continue;
999 }
1000 agent = agent_pvt_new(cfg);
1001 if (!agent) {
1002 continue;
1003 }
1004 ao2_link(agents, agent);
1005 ast_debug(1, "Agent %s: Created.\n", agent->username);
1006 agent_devstate_changed(agent->username);
1007 }
1008 ao2_iterator_destroy(&iter);
1009 agents_sweep();
1010}
1011
1012static int agent_logoff_request(const char *agent_id, int soft)
1013{
1014 struct ast_channel *logged;
1015 RAII_VAR(struct agent_pvt *, agent, ao2_find(agents, agent_id, OBJ_KEY), ao2_cleanup);
1016
1017 if (!agent) {
1018 return -1;
1019 }
1020
1021 agent_lock(agent);
1022 logged = agent_lock_logged(agent);
1023 if (logged) {
1024 if (soft) {
1025 agent->deferred_logoff = 1;
1026 } else {
1028 }
1029 ast_channel_unlock(logged);
1030 ast_channel_unref(logged);
1031 }
1032 agent_unlock(agent);
1033 return 0;
1034}
1035
1036/*! Agent holding bridge instance holder. */
1037static AO2_GLOBAL_OBJ_STATIC(agent_holding);
1038
1039/*! Agent holding bridge deferred creation lock. */
1041
1042/*!
1043 * \internal
1044 * \brief Callback to clear AGENT_STATUS on the caller channel.
1045 *
1046 * \param bridge_channel Which channel to operate on.
1047 * \param payload Data to pass to the callback. (NULL if none).
1048 * \param payload_size Size of the payload if payload is non-NULL. A number otherwise.
1049 *
1050 * \note The payload MUST NOT have any resources that need to be freed.
1051 */
1052static void clear_agent_status(struct ast_bridge_channel *bridge_channel, const void *payload, size_t payload_size)
1053{
1055}
1056
1057/*!
1058 * \internal
1059 * \brief Connect the agent with the waiting caller.
1060 * \since 12.0.0
1061 *
1062 * \param bridge_channel Agent channel connecting to the caller.
1063 * \param agent Which agent is connecting to the caller.
1064 *
1065 * \note The agent is locked on entry and not locked on exit.
1066 */
1068{
1069 struct ast_bridge *caller_bridge;
1070 int record_agent_calls;
1071 int res;
1072
1073 record_agent_calls = agent->cfg->record_agent_calls;
1074 caller_bridge = agent->caller_bridge;
1075 agent->caller_bridge = NULL;
1076 agent->state = AGENT_STATE_ON_CALL;
1077 time(&agent->call_start);
1078 agent_unlock(agent);
1079
1080 if (!caller_bridge) {
1081 /* Reset agent. */
1084 return;
1085 }
1086 res = ast_bridge_move(caller_bridge, bridge_channel->bridge, bridge_channel->chan,
1087 NULL, 0);
1088 if (res) {
1089 /* Reset agent. */
1090 ast_bridge_destroy(caller_bridge, 0);
1093 return;
1094 }
1097 if (res) {
1098 /* Reset agent. */
1099 ast_bridge_destroy(caller_bridge, 0);
1100 return;
1101 }
1102
1103 if (record_agent_calls) {
1105 .start_stop = AUTO_MONITOR_START,
1106 };
1107
1108 /*
1109 * The agent is in the new bridge so we can invoke the
1110 * mixmonitor hook to only start recording.
1111 */
1113 }
1114
1115 ao2_t_ref(caller_bridge, -1, "Agent successfully in caller_bridge");
1116}
1117
1118static int bridge_agent_hold_ack(struct ast_bridge_channel *bridge_channel, void *hook_pvt)
1119{
1120 struct agent_pvt *agent = hook_pvt;
1121
1122 agent_lock(agent);
1123 switch (agent->state) {
1125 /* Connect to caller now. */
1126 ast_debug(1, "Agent %s: Acked call.\n", agent->username);
1127 agent_connect_caller(bridge_channel, agent);/* Will unlock agent. */
1128 return 0;
1129 default:
1130 break;
1131 }
1132 agent_unlock(agent);
1133 return 0;
1134}
1135
1136static int bridge_agent_hold_heartbeat(struct ast_bridge_channel *bridge_channel, void *hook_pvt)
1137{
1138 struct agent_pvt *agent = hook_pvt;
1139 int probation_timedout = 0;
1140 int ack_timedout = 0;
1141 int wrapup_timedout = 0;
1142 int deferred_logoff;
1143 unsigned int wrapup_time;
1144 unsigned int auto_logoff;
1145
1146 agent_lock(agent);
1148 if (deferred_logoff) {
1150 }
1151
1152 switch (agent->state) {
1154 probation_timedout =
1155 LOGIN_WAIT_TIMEOUT_TIME <= (time(NULL) - agent->probation_start);
1156 if (probation_timedout) {
1157 /* Now ready for a caller. */
1160 }
1161 break;
1163 /* Check ack call time. */
1164 auto_logoff = agent->cfg->auto_logoff;
1166 auto_logoff = agent->override_auto_logoff;
1167 }
1168 if (auto_logoff) {
1169 auto_logoff *= 1000;
1170 ack_timedout = ast_tvdiff_ms(ast_tvnow(), agent->ack_time) > auto_logoff;
1171 if (ack_timedout) {
1173 }
1174 }
1175 break;
1177 /* Check wrapup time. */
1178 wrapup_time = agent->cfg->wrapup_time;
1180 wrapup_time = agent->override_wrapup_time;
1181 }
1182 wrapup_timedout = ast_tvdiff_ms(ast_tvnow(), agent->last_disconnect) > wrapup_time;
1183 if (wrapup_timedout) {
1186 }
1187 break;
1188 default:
1189 break;
1190 }
1191 agent_unlock(agent);
1192
1193 if (deferred_logoff) {
1194 ast_debug(1, "Agent %s: Deferred logoff.\n", agent->username);
1197 } else if (probation_timedout) {
1198 ast_debug(1, "Agent %s: Login complete.\n", agent->username);
1200 } else if (ack_timedout) {
1201 ast_debug(1, "Agent %s: Ack call timeout.\n", agent->username);
1204 } else if (wrapup_timedout) {
1205 ast_debug(1, "Agent %s: Wrapup timeout. Ready for new call.\n", agent->username);
1207 }
1208
1209 return 0;
1210}
1211
1212static void agent_after_bridge_cb(struct ast_channel *chan, void *data);
1213static void agent_after_bridge_cb_failed(enum ast_bridge_after_cb_reason reason, void *data);
1214
1215/*!
1216 * \internal
1217 * \brief ast_bridge agent_hold push method.
1218 * \since 12.0.0
1219 *
1220 * \param self Bridge to operate upon.
1221 * \param bridge_channel Bridge channel to push.
1222 * \param swap Bridge channel to swap places with if not NULL.
1223 *
1224 * \note On entry, self is already locked.
1225 *
1226 * \retval 0 on success
1227 * \retval -1 on failure
1228 */
1229static int bridge_agent_hold_push(struct ast_bridge *self, struct ast_bridge_channel *bridge_channel, struct ast_bridge_channel *swap)
1230{
1231 int res = 0;
1232 unsigned int wrapup_time;
1233 char dtmf[AST_FEATURE_MAX_LEN];
1234 struct ast_channel *chan;
1235 const char *moh_class;
1236 RAII_VAR(struct agent_pvt *, agent, NULL, ao2_cleanup);
1237
1238 chan = bridge_channel->chan;
1239
1240 agent = ao2_find(agents, swap ? swap->chan : chan, 0);
1241 if (!agent) {
1242 /* Could not find the agent. */
1243 return -1;
1244 }
1245
1246 /* Setup agent entertainment */
1247 agent_lock(agent);
1248 moh_class = ast_strdupa(agent->cfg->moh);
1249 agent_unlock(agent);
1250 res |= ast_channel_add_bridge_role(chan, "holding_participant");
1251 res |= ast_channel_set_bridge_role_option(chan, "holding_participant", "idle_mode", "musiconhold");
1252 res |= ast_channel_set_bridge_role_option(chan, "holding_participant", "moh_class", moh_class);
1253
1254 /* Add DTMF acknowledge hook. */
1255 dtmf[0] = '\0';
1256 agent_lock(agent);
1258 ? agent->override_ack_call : agent->cfg->ack_call) {
1259 const char *dtmf_accept;
1260
1261 dtmf_accept = ast_test_flag(agent, AGENT_FLAG_DTMF_ACCEPT)
1262 ? agent->override_dtmf_accept : agent->cfg->dtmf_accept;
1263 ast_copy_string(dtmf, dtmf_accept, sizeof(dtmf));
1264 }
1265 agent_unlock(agent);
1266 if (!ast_strlen_zero(dtmf)) {
1267 ao2_ref(agent, +1);
1270 ao2_ref(agent, -1);
1271 res = -1;
1272 }
1273 }
1274
1275 /* Add heartbeat interval hook. */
1276 ao2_ref(agent, +1);
1279 ao2_ref(agent, -1);
1280 res = -1;
1281 }
1282
1283 res |= ast_bridge_base_v_table.push(self, bridge_channel, swap);
1284 if (res) {
1285 ast_channel_remove_bridge_role(chan, "holding_participant");
1286 return -1;
1287 }
1288
1289 if (swap) {
1292 if (res) {
1293 ast_channel_remove_bridge_role(chan, "holding_participant");
1294 return -1;
1295 }
1296
1297 agent_lock(agent);
1298 ast_channel_unref(agent->logged);
1299 agent->logged = ast_channel_ref(chan);
1300 agent_unlock(agent);
1301
1302 /*
1303 * Kick the channel out so it can come back in fully controlled.
1304 * Otherwise, the after bridge callback will linger and the
1305 * agent will have some slightly different behavior in corner
1306 * cases.
1307 */
1310 return 0;
1311 }
1312
1313 agent_lock(agent);
1314 switch (agent->state) {
1316 /*!
1317 * \todo XXX the login probation time should be only if it is needed.
1318 *
1319 * Need to determine if there are any local channels that can
1320 * optimize and wait until they actually do before leaving the
1321 * AGENT_STATE_PROBATION_WAIT state. For now, the blind
1322 * timer of LOGIN_WAIT_TIMEOUT_TIME will do.
1323 */
1324 /*
1325 * Start the login probation timer.
1326 *
1327 * We cannot handle an agent local channel optimization when the
1328 * agent is on a call. The optimization may kick the agent
1329 * channel we know about out of the call without our being able
1330 * to switch to the replacement channel. Get any agent local
1331 * channel optimization out of the way while the agent is in the
1332 * holding bridge.
1333 */
1334 time(&agent->probation_start);
1335 agent->state = AGENT_STATE_PROBATION_WAIT;
1336 agent_unlock(agent);
1337 break;
1339 /* Restart the probation timer. */
1340 time(&agent->probation_start);
1341 agent_unlock(agent);
1342 break;
1344 /*
1345 * Likely someone manually kicked us out of the holding bridge
1346 * and we came right back in.
1347 */
1348 agent_unlock(agent);
1349 break;
1350 default:
1351 /* Unexpected agent state. */
1352 ast_assert(0);
1353 /* Fall through */
1356 agent->state = AGENT_STATE_READY_FOR_CALL;
1357 agent->devstate = AST_DEVICE_NOT_INUSE;
1358 agent_unlock(agent);
1359 ast_debug(1, "Agent %s: Call abort recovery complete.\n", agent->username);
1360 agent_devstate_changed(agent->username);
1361 break;
1364 wrapup_time = agent->cfg->wrapup_time;
1366 wrapup_time = agent->override_wrapup_time;
1367 }
1368 if (wrapup_time) {
1369 agent->state = AGENT_STATE_CALL_WRAPUP;
1370 } else {
1371 agent->state = AGENT_STATE_READY_FOR_CALL;
1372 agent->devstate = AST_DEVICE_NOT_INUSE;
1373 }
1374 agent_unlock(agent);
1375 if (!wrapup_time) {
1376 /* No wrapup time. */
1377 ast_debug(1, "Agent %s: Ready for new call.\n", agent->username);
1378 agent_devstate_changed(agent->username);
1379 }
1380 break;
1381 }
1382
1383 return 0;
1384}
1385
1386/*!
1387 * \internal
1388 * \brief ast_bridge agent_hold pull method.
1389 *
1390 * \param self Bridge to operate upon.
1391 * \param bridge_channel Bridge channel to pull.
1392 *
1393 * \details
1394 * Remove any channel hooks controlled by the bridge. Release
1395 * any resources held by bridge_channel->bridge_pvt and release
1396 * bridge_channel->bridge_pvt.
1397 *
1398 * \note On entry, self is already locked.
1399 */
1401{
1402 ast_channel_remove_bridge_role(bridge_channel->chan, "holding_participant");
1404}
1405
1406/*!
1407 * \brief The bridge is being dissolved.
1408 *
1409 * \param self Bridge to operate upon.
1410 *
1411 * \details
1412 * The bridge is being dissolved. Remove any external
1413 * references to the bridge so it can be destroyed.
1414 *
1415 * \note On entry, self must NOT be locked.
1416 */
1418{
1419 ao2_global_obj_release(agent_holding);
1421}
1422
1424
1426{
1427 struct ast_bridge *bridge;
1428
1429 bridge = bridge_alloc(sizeof(struct ast_bridge), &bridge_agent_hold_v_table);
1433 "AgentPool", NULL, NULL);
1434 bridge = bridge_register(bridge);
1435 return bridge;
1436}
1437
1438static void bridge_init_agent_hold(void)
1439{
1440 /* Setup bridge agent_hold subclass v_table. */
1442 bridge_agent_hold_v_table.name = "agent_hold";
1446}
1447
1449{
1450 RAII_VAR(struct ast_bridge *, holding, ao2_global_obj_ref(agent_holding), ao2_cleanup);
1451
1452 if (!holding) {
1454 holding = ao2_global_obj_ref(agent_holding);
1455 if (!holding) {
1456 holding = bridge_agent_hold_new();
1457 ao2_global_obj_replace_unref(agent_holding, holding);
1458 }
1460 if (!holding) {
1461 ast_log(LOG_ERROR, "Could not create agent holding bridge.\n");
1462 return -1;
1463 }
1464 }
1465 return 0;
1466}
1467
1468static void send_agent_login(struct ast_channel *chan, const char *agent)
1469{
1470 RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
1471
1472 ast_assert(agent != NULL);
1473
1474 blob = ast_json_pack("{s: s}",
1475 "agent", agent);
1476 if (!blob) {
1477 return;
1478 }
1479
1481}
1482
1483static void send_agent_logoff(struct ast_channel *chan, const char *agent, long logintime)
1484{
1485 RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
1486
1487 ast_assert(agent != NULL);
1488
1489 blob = ast_json_pack("{s: s, s: I}",
1490 "agent", agent,
1491 "logintime", (ast_json_int_t)logintime);
1492 if (!blob) {
1493 return;
1494 }
1495
1497}
1498
1499/*!
1500 * \internal
1501 * \brief Logout the agent.
1502 * \since 12.0.0
1503 *
1504 * \param agent Which agent logging out.
1505 *
1506 * \note On entry agent is already locked. On exit it is no longer locked.
1507 */
1508static void agent_logout(struct agent_pvt *agent)
1509{
1510 struct ast_channel *logged;
1511 struct ast_bridge *caller_bridge;
1512 long time_logged_in;
1513
1514 time_logged_in = time(NULL) - agent->login_start;
1515 logged = agent->logged;
1516 agent->logged = NULL;
1517 caller_bridge = agent->caller_bridge;
1518 agent->caller_bridge = NULL;
1522 agent_unlock(agent);
1524
1525 if (caller_bridge) {
1526 ast_bridge_destroy(caller_bridge, 0);
1527 }
1528
1529 ast_channel_lock(logged);
1530 send_agent_logoff(logged, agent->username, time_logged_in);
1531 ast_channel_unlock(logged);
1532 ast_verb(2, "Agent '%s' logged out. Logged in for %ld seconds.\n",
1533 agent->username, time_logged_in);
1534 ast_channel_unref(logged);
1535}
1536
1537/*!
1538 * \internal
1539 * \brief Agent driver loop.
1540 * \since 12.0.0
1541 *
1542 * \param agent Which agent.
1543 * \param logged The logged in channel.
1544 */
1545static void agent_run(struct agent_pvt *agent, struct ast_channel *logged)
1546{
1547 struct ast_bridge_features features;
1548
1549 if (ast_bridge_features_init(&features)) {
1551 goto agent_run_cleanup;
1552 }
1553 for (;;) {
1554 struct agents_cfg *cfgs;
1555 struct agent_cfg *cfg_new;
1556 struct agent_cfg *cfg_old;
1557 struct ast_bridge *holding;
1558 struct ast_bridge *caller_bridge;
1559
1561
1562 holding = ao2_global_obj_ref(agent_holding);
1563 if (!holding) {
1564 ast_debug(1, "Agent %s: Someone destroyed the agent holding bridge.\n",
1565 agent->username);
1566 break;
1567 }
1568
1569 /*
1570 * When the agent channel leaves the bridging system we usually
1571 * want to put the agent back into the holding bridge for the
1572 * next caller.
1573 */
1574 ast_bridge_join(holding, logged, NULL, &features, NULL,
1576 if (logged != agent->logged) {
1577 /* This channel is no longer the logged in agent. */
1578 break;
1579 }
1580
1581 if (agent->dead) {
1582 /* The agent is no longer configured. */
1583 break;
1584 }
1585
1586 /* Update the agent's config before rejoining the holding bridge. */
1588 if (!cfgs) {
1589 /* There is no agent configuration. All agents were destroyed. */
1590 break;
1591 }
1592 cfg_new = ao2_find(cfgs->agents, agent->username, OBJ_KEY);
1593 ao2_ref(cfgs, -1);
1594 if (!cfg_new) {
1595 /* The agent is no longer configured. */
1596 break;
1597 }
1598 agent_lock(agent);
1599 cfg_old = agent->cfg;
1600 agent->cfg = cfg_new;
1601
1602 agent->last_disconnect = ast_tvnow();
1603
1604 /* Clear out any caller bridge before rejoining the holding bridge. */
1605 caller_bridge = agent->caller_bridge;
1606 agent->caller_bridge = NULL;
1607 agent_unlock(agent);
1608 ao2_ref(cfg_old, -1);
1609 if (caller_bridge) {
1610 ast_bridge_destroy(caller_bridge, 0);
1611 }
1612
1613 if (agent->state == AGENT_STATE_LOGGING_OUT
1614 || agent->deferred_logoff
1615 || ast_check_hangup_locked(logged)) {
1616 /* The agent was requested to logout or hungup. */
1617 break;
1618 }
1619
1620 /*
1621 * It is safe to access agent->waiting_colp without a lock. It
1622 * is only setup on agent login and not changed.
1623 */
1625 }
1626 ast_bridge_features_cleanup(&features);
1627
1628agent_run_cleanup:
1629 agent_lock(agent);
1630 if (logged != agent->logged) {
1631 /*
1632 * We are no longer the agent channel because of local channel
1633 * optimization.
1634 */
1635 agent_unlock(agent);
1636 ast_debug(1, "Agent %s: Channel %s is no longer the agent.\n",
1637 agent->username, ast_channel_name(logged));
1638 return;
1639 }
1640 agent_logout(agent);
1641}
1642
1643static void agent_after_bridge_cb(struct ast_channel *chan, void *data)
1644{
1645 struct agent_pvt *agent;
1646
1647 agent = ao2_find(agents, chan, 0);
1648 if (!agent) {
1649 return;
1650 }
1651
1652 ast_debug(1, "Agent %s: New agent channel %s.\n",
1653 agent->username, ast_channel_name(chan));
1654 agent_run(agent, chan);
1655 ao2_ref(agent, -1);
1656}
1657
1659{
1660 struct ast_channel *chan = data;
1661 struct agent_pvt *agent;
1662
1663 agent = ao2_find(agents, chan, 0);
1664 if (!agent) {
1665 return;
1666 }
1667 ast_log(LOG_WARNING, "Agent %s: Forced logout. Lost control of %s because: %s\n",
1668 agent->username, ast_channel_name(chan),
1670 agent_lock(agent);
1671 agent_logout(agent);
1672 ao2_ref(agent, -1);
1673}
1674
1675/*!
1676 * \internal
1677 * \brief Get the lock on the agent bridge_channel and return it.
1678 * \since 12.0.0
1679 *
1680 * \param agent Whose bridge_channel to get.
1681 *
1682 * \retval bridge_channel on success (Reffed and locked).
1683 * \retval NULL on error.
1684 */
1686{
1687 struct ast_channel *logged;
1688 struct ast_bridge_channel *bc;
1689
1690 for (;;) {
1691 agent_lock(agent);
1692 logged = agent->logged;
1693 if (!logged) {
1694 agent_unlock(agent);
1695 return NULL;
1696 }
1697 ast_channel_ref(logged);
1698 agent_unlock(agent);
1699
1700 ast_channel_lock(logged);
1702 ast_channel_unlock(logged);
1703 ast_channel_unref(logged);
1704 if (!bc) {
1705 if (agent->logged != logged) {
1706 continue;
1707 }
1708 return NULL;
1709 }
1710
1712 if (bc->chan != logged || agent->logged != logged) {
1714 ao2_ref(bc, -1);
1715 continue;
1716 }
1717 return bc;
1718 }
1719}
1720
1721static void caller_abort_agent(struct agent_pvt *agent)
1722{
1723 struct ast_bridge_channel *logged;
1724
1725 logged = agent_bridge_channel_get_lock(agent);
1726 if (!logged) {
1727 struct ast_bridge *caller_bridge;
1728
1729 ast_debug(1, "Agent '%s' no longer logged in.\n", agent->username);
1730
1731 agent_lock(agent);
1732 caller_bridge = agent->caller_bridge;
1733 agent->caller_bridge = NULL;
1734 agent_unlock(agent);
1735 if (caller_bridge) {
1736 ast_bridge_destroy(caller_bridge, 0);
1737 }
1738 return;
1739 }
1740
1741 /* Kick the agent out of the holding bridge to reset it. */
1745}
1746
1747static int caller_safety_timeout(struct ast_bridge_channel *bridge_channel, void *hook_pvt)
1748{
1749 struct agent_pvt *agent = hook_pvt;
1750
1751 if (agent->state == AGENT_STATE_CALL_PRESENT) {
1752 ast_log(LOG_WARNING, "Agent '%s' process did not respond. Safety timeout.\n",
1753 agent->username);
1754 pbx_builtin_setvar_helper(bridge_channel->chan, "AGENT_STATUS", "ERROR");
1755
1757 caller_abort_agent(agent);
1758 }
1759
1760 return -1;
1761}
1762
1763static void agent_alert(struct ast_bridge_channel *bridge_channel, const void *payload, size_t payload_size)
1764{
1765 const char *agent_id = payload;
1766 const char *playfile;
1767 const char *dtmf_accept;
1768 struct agent_pvt *agent;
1769 int digit;
1770 char dtmf[2];
1771
1772 agent = ao2_find(agents, agent_id, OBJ_KEY);
1773 if (!agent) {
1774 ast_debug(1, "Agent '%s' does not exist. Where did it go?\n", agent_id);
1775 return;
1776 }
1777
1778 /* Change holding bridge participant role's idle mode to silence */
1779 ast_bridge_channel_lock_bridge(bridge_channel);
1780 ast_bridge_channel_clear_roles(bridge_channel);
1781 ast_channel_set_bridge_role_option(bridge_channel->chan, "holding_participant", "idle_mode", "silence");
1782 ast_bridge_channel_establish_roles(bridge_channel);
1783 ast_bridge_unlock(bridge_channel->bridge);
1784
1785 agent_lock(agent);
1786 playfile = ast_strdupa(agent->cfg->beep_sound);
1787
1788 /* Determine which DTMF digits interrupt the alerting signal. */
1790 ? agent->override_ack_call : agent->cfg->ack_call) {
1791 dtmf_accept = ast_test_flag(agent, AGENT_FLAG_DTMF_ACCEPT)
1792 ? agent->override_dtmf_accept : agent->cfg->dtmf_accept;
1793
1794 /* Only the first digit of the ack will stop playback. */
1795 dtmf[0] = *dtmf_accept;
1796 dtmf[1] = '\0';
1797 dtmf_accept = dtmf;
1798 } else {
1799 dtmf_accept = NULL;
1800 }
1801 agent_unlock(agent);
1802
1803 /* Alert the agent. */
1804 digit = ast_stream_and_wait(bridge_channel->chan, playfile,
1805 ast_strlen_zero(dtmf_accept) ? AST_DIGIT_ANY : dtmf_accept);
1806 ast_stopstream(bridge_channel->chan);
1807
1808 agent_lock(agent);
1809 switch (agent->state) {
1811 if (!ast_strlen_zero(dtmf_accept)) {
1813 agent->ack_time = ast_tvnow();
1814
1815 if (0 < digit) {
1816 /* Playback was interrupted by a digit. */
1817 agent_unlock(agent);
1818 ao2_ref(agent, -1);
1819 ast_bridge_channel_feature_digit(bridge_channel, digit);
1820 return;
1821 }
1822 break;
1823 }
1824
1825 /* Connect to caller now. */
1826 ast_debug(1, "Agent %s: Immediately connecting to call.\n", agent->username);
1827 agent_connect_caller(bridge_channel, agent);/* Will unlock agent. */
1828 ao2_ref(agent, -1);
1829 return;
1830 default:
1831 break;
1832 }
1833 agent_unlock(agent);
1834 ao2_ref(agent, -1);
1835}
1836
1837static int send_alert_to_agent(struct ast_bridge_channel *bridge_channel, const char *agent_id)
1838{
1839 return ast_bridge_channel_queue_callback(bridge_channel,
1840 AST_BRIDGE_CHANNEL_CB_OPTION_MEDIA, agent_alert, agent_id, strlen(agent_id) + 1);
1841}
1842
1844{
1846 .id.name = 1,
1847 .id.number = 1,
1848 .id.subaddress = 1,
1849 };
1850 unsigned char data[1024]; /* This should be large enough */
1851 size_t datalen;
1852
1853 datalen = ast_connected_line_build_data(data, sizeof(data), connected, &update);
1854 if (datalen == (size_t) -1) {
1855 return 0;
1856 }
1857
1858 return ast_bridge_channel_queue_control_data(bridge_channel,
1859 AST_CONTROL_CONNECTED_LINE, data, datalen);
1860}
1861
1862/*!
1863 * \internal
1864 * \brief Caller joined the bridge event callback.
1865 *
1866 * \param bridge_channel Channel executing the feature
1867 * \param hook_pvt Private data passed in when the hook was created
1868 *
1869 * \retval 0 Keep the callback hook.
1870 * \retval -1 Remove the callback hook.
1871 */
1872static int caller_joined_bridge(struct ast_bridge_channel *bridge_channel, void *hook_pvt)
1873{
1874 struct agent_pvt *agent = hook_pvt;
1875 struct ast_bridge_channel *logged;
1876 int res;
1877
1878 logged = agent_bridge_channel_get_lock(agent);
1879 if (!logged) {
1880 ast_verb(3, "Agent '%s' not logged in.\n", agent->username);
1881 pbx_builtin_setvar_helper(bridge_channel->chan, "AGENT_STATUS", "NOT_LOGGED_IN");
1882
1884 caller_abort_agent(agent);
1885 return -1;
1886 }
1887
1888 res = send_alert_to_agent(logged, agent->username);
1890 ao2_ref(logged, -1);
1891 if (res) {
1892 ast_verb(3, "Agent '%s': Failed to alert the agent.\n", agent->username);
1893 pbx_builtin_setvar_helper(bridge_channel->chan, "AGENT_STATUS", "ERROR");
1894
1896 caller_abort_agent(agent);
1897 return -1;
1898 }
1899
1900 pbx_builtin_setvar_helper(bridge_channel->chan, "AGENT_STATUS", "NOT_CONNECTED");
1901 ast_indicate(bridge_channel->chan, AST_CONTROL_RINGING);
1902 return -1;
1903}
1904
1905/*!
1906 * \brief Dialplan AgentRequest application to locate an agent to talk with.
1907 *
1908 * \param chan Channel wanting to talk with an agent.
1909 * \param data Application parameters
1910 *
1911 * \retval 0 To continue in dialplan.
1912 * \retval -1 To hangup.
1913 */
1914static int agent_request_exec(struct ast_channel *chan, const char *data)
1915{
1916 struct ast_bridge *caller_bridge;
1917 struct ast_bridge_channel *logged;
1918 char *parse;
1919 int res;
1920 struct ast_bridge_features caller_features;
1923 AST_APP_ARG(agent_id);
1924 AST_APP_ARG(other); /* Any remaining unused arguments */
1925 );
1926
1927 RAII_VAR(struct agent_pvt *, agent, NULL, ao2_cleanup);
1928
1930 return -1;
1931 }
1932
1933 parse = ast_strdupa(data);
1935
1936 if (ast_strlen_zero(args.agent_id)) {
1937 ast_log(LOG_WARNING, "AgentRequest requires an AgentId\n");
1938 return -1;
1939 }
1940
1941 /* Find the agent. */
1942 agent = ao2_find(agents, args.agent_id, OBJ_KEY);
1943 if (!agent) {
1944 ast_verb(3, "Agent '%s' does not exist.\n", args.agent_id);
1945 pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "INVALID");
1946 return 0;
1947 }
1948
1949 if (ast_bridge_features_init(&caller_features)) {
1950 return -1;
1951 }
1952
1953 /* Add safety timeout hook. */
1954 ao2_ref(agent, +1);
1957 ao2_ref(agent, -1);
1958 ast_bridge_features_cleanup(&caller_features);
1959 return -1;
1960 }
1961
1962 /* Setup the alert agent on caller joining the bridge hook. */
1963 ao2_ref(agent, +1);
1964 if (ast_bridge_join_hook(&caller_features, caller_joined_bridge, agent,
1965 __ao2_cleanup, 0)) {
1966 ao2_ref(agent, -1);
1967 ast_bridge_features_cleanup(&caller_features);
1968 return -1;
1969 }
1970
1971 caller_bridge = ast_bridge_basic_new();
1972 if (!caller_bridge) {
1973 ast_bridge_features_cleanup(&caller_features);
1974 return -1;
1975 }
1976
1977 agent_lock(agent);
1978 switch (agent->state) {
1981 agent_unlock(agent);
1982 ast_bridge_destroy(caller_bridge, 0);
1983 ast_bridge_features_cleanup(&caller_features);
1984 ast_verb(3, "Agent '%s' not logged in.\n", agent->username);
1985 pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "NOT_LOGGED_IN");
1986 return 0;
1988 ao2_ref(caller_bridge, +1);
1989 agent->caller_bridge = caller_bridge;
1990 agent->state = AGENT_STATE_CALL_PRESENT;
1991 agent->devstate = AST_DEVICE_INUSE;
1992 break;
1993 default:
1994 agent_unlock(agent);
1995 ast_bridge_destroy(caller_bridge, 0);
1996 ast_bridge_features_cleanup(&caller_features);
1997 ast_verb(3, "Agent '%s' is busy.\n", agent->username);
1998 pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "BUSY");
1999 return 0;
2000 }
2001 agent_unlock(agent);
2002 agent_devstate_changed(agent->username);
2003
2004 /* Get COLP for agent. */
2006 ast_channel_lock(chan);
2008 ast_channel_unlock(chan);
2009
2010 logged = agent_bridge_channel_get_lock(agent);
2011 if (!logged) {
2013 caller_abort_agent(agent);
2014 ast_bridge_destroy(caller_bridge, 0);
2015 ast_bridge_features_cleanup(&caller_features);
2016 ast_verb(3, "Agent '%s' not logged in.\n", agent->username);
2017 pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "NOT_LOGGED_IN");
2018 return 0;
2019 }
2020
2021 send_colp_to_agent(logged, &connected);
2023 ao2_ref(logged, -1);
2025
2026 if (ast_bridge_join(caller_bridge, chan, NULL, &caller_features, NULL,
2028 caller_abort_agent(agent);
2029 ast_verb(3, "Agent '%s': Caller %s failed to join the bridge.\n",
2030 agent->username, ast_channel_name(chan));
2031 pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "ERROR");
2032 }
2033 ast_bridge_features_cleanup(&caller_features);
2034
2035 /* Determine if we need to continue in the dialplan after the bridge. */
2036 ast_channel_lock(chan);
2038 /*
2039 * The bridge was broken for a hangup that isn't real.
2040 * Don't run the h extension, because the channel isn't
2041 * really hung up. This should really only happen with
2042 * AST_SOFTHANGUP_ASYNCGOTO.
2043 */
2044 res = 0;
2045 } else {
2046 res = ast_check_hangup(chan)
2048 || ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENT_STATUS"));
2049 }
2050 ast_channel_unlock(chan);
2051
2052 return res ? -1 : 0;
2053}
2054
2055/*!
2056 * \internal
2057 * \brief Get agent config values from the login channel.
2058 * \since 12.0.0
2059 *
2060 * \param agent What to setup channel config values on.
2061 * \param chan Channel logging in as an agent.
2062 */
2063static void agent_login_channel_config(struct agent_pvt *agent, struct ast_channel *chan)
2064{
2065 struct ast_flags opts = { 0 };
2067 unsigned int override_ack_call = 0;
2068 unsigned int override_auto_logoff = 0;
2069 unsigned int override_wrapup_time = 0;
2070 const char *override_dtmf_accept = NULL;
2071 const char *var;
2072
2074
2075 /* Get config values from channel. */
2076 ast_channel_lock(chan);
2078
2079 var = pbx_builtin_getvar_helper(chan, "AGENTACKCALL");
2080 if (!ast_strlen_zero(var)) {
2081 override_ack_call = ast_true(var) ? 1 : 0;
2083 }
2084
2085 var = pbx_builtin_getvar_helper(chan, "AGENTACCEPTDTMF");
2086 if (!ast_strlen_zero(var)) {
2087 override_dtmf_accept = ast_strdupa(var);
2089 }
2090
2091 var = pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF");
2092 if (!ast_strlen_zero(var)) {
2093 if (sscanf(var, "%u", &override_auto_logoff) == 1) {
2095 }
2096 }
2097
2098 var = pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME");
2099 if (!ast_strlen_zero(var)) {
2100 if (sscanf(var, "%u", &override_wrapup_time) == 1) {
2102 }
2103 }
2104 ast_channel_unlock(chan);
2105
2106 /* Set config values on agent. */
2107 agent_lock(agent);
2109 agent->waiting_colp = connected;
2110
2111 ast_string_field_set(agent, override_dtmf_accept, override_dtmf_accept);
2112 ast_copy_flags(agent, &opts, AST_FLAGS_ALL);
2113 agent->override_auto_logoff = override_auto_logoff;
2114 agent->override_wrapup_time = override_wrapup_time;
2115 agent->override_ack_call = override_ack_call;
2116 agent_unlock(agent);
2117}
2118
2120 OPT_SILENT = (1 << 0),
2121};
2125
2126/*!
2127 * \brief Dialplan AgentLogin application to log in an agent.
2128 *
2129 * \param chan Channel attempting to login as an agent.
2130 * \param data Application parameters
2131 *
2132 * \retval 0 To continue in dialplan.
2133 * \retval -1 To hangup.
2134 */
2135static int agent_login_exec(struct ast_channel *chan, const char *data)
2136{
2137 char *parse;
2138 struct ast_flags opts;
2140 AST_APP_ARG(agent_id);
2142 AST_APP_ARG(other); /* Any remaining unused arguments */
2143 );
2144
2145 RAII_VAR(struct agent_pvt *, agent, NULL, ao2_cleanup);
2146
2148 return -1;
2149 }
2150
2151 if (ast_channel_state(chan) != AST_STATE_UP && ast_answer(chan)) {
2152 return -1;
2153 }
2154
2155 parse = ast_strdupa(data);
2157
2158 if (ast_strlen_zero(args.agent_id)) {
2159 ast_log(LOG_WARNING, "AgentLogin requires an AgentId\n");
2160 return -1;
2161 }
2162
2163 if (ast_app_parse_options(agent_login_opts, &opts, NULL, args.options)) {
2164 /* General invalid option syntax. */
2165 return -1;
2166 }
2167
2168 /* Find the agent. */
2169 agent = ao2_find(agents, args.agent_id, OBJ_KEY);
2170 if (!agent) {
2171 ast_verb(3, "Agent '%s' does not exist.\n", args.agent_id);
2172 pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "INVALID");
2173 return 0;
2174 }
2175
2176 /* Has someone already logged in as this agent already? */
2177 agent_lock(agent);
2178 if (agent->logged) {
2179 agent_unlock(agent);
2180 ast_verb(3, "Agent '%s' already logged in.\n", agent->username);
2181 pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "ALREADY_LOGGED_IN");
2182 return 0;
2183 }
2184 agent->logged = ast_channel_ref(chan);
2185 agent->last_disconnect = ast_tvnow();
2186 time(&agent->login_start);
2187 agent->deferred_logoff = 0;
2188 agent_unlock(agent);
2189
2190 agent_login_channel_config(agent, chan);
2191
2192 if (!ast_test_flag(&opts, OPT_SILENT)) {
2193 ast_stream_and_wait(chan, "agent-loginok", AST_DIGIT_NONE);
2194 }
2195
2196 ast_verb(2, "Agent '%s' logged in (format %s/%s)\n", agent->username,
2199 ast_channel_lock(chan);
2200 send_agent_login(chan, agent->username);
2201 ast_channel_unlock(chan);
2202
2203 agent_run(agent, chan);
2204 return -1;
2205}
2206
2207static int agent_function_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
2208{
2209 char *parse;
2210 struct agent_pvt *agent;
2211 struct ast_channel *logged;
2213 AST_APP_ARG(agentid);
2215 );
2216
2217 buf[0] = '\0';
2218
2219 parse = ast_strdupa(data ?: "");
2220 AST_NONSTANDARD_APP_ARGS(args, parse, ':');
2221
2222 if (ast_strlen_zero(args.agentid)) {
2223 ast_log(LOG_WARNING, "The AGENT function requires an argument - agentid!\n");
2224 return -1;
2225 }
2226 if (!args.item) {
2227 args.item = "status";
2228 }
2229
2230 agent = ao2_find(agents, args.agentid, OBJ_KEY);
2231 if (!agent) {
2232 ast_log(LOG_WARNING, "Agent '%s' not found!\n", args.agentid);
2233 return -1;
2234 }
2235
2236 agent_lock(agent);
2237 if (!strcasecmp(args.item, "status")) {
2238 const char *status;
2239
2240 if (agent->logged) {
2241 status = "LOGGEDIN";
2242 } else {
2243 status = "LOGGEDOUT";
2244 }
2246 } else if (!strcasecmp(args.item, "name")) {
2247 ast_copy_string(buf, agent->cfg->full_name, len);
2248 } else if (!strcasecmp(args.item, "mohclass")) {
2249 ast_copy_string(buf, agent->cfg->moh, len);
2250 } else if (!strcasecmp(args.item, "channel")) {
2251 logged = agent_lock_logged(agent);
2252 if (logged) {
2253 char *pos;
2254
2256 ast_channel_unlock(logged);
2257 ast_channel_unref(logged);
2258
2259 pos = strrchr(buf, '-');
2260 if (pos) {
2261 *pos = '\0';
2262 }
2263 }
2264 } else if (!strcasecmp(args.item, "fullchannel")) {
2265 logged = agent_lock_logged(agent);
2266 if (logged) {
2268 ast_channel_unlock(logged);
2269 ast_channel_unref(logged);
2270 }
2271 }
2272 agent_unlock(agent);
2273 ao2_ref(agent, -1);
2274
2275 return 0;
2276}
2277
2279 .name = "AGENT",
2280 .read = agent_function_read,
2281};
2282
2284 /*! Nth match to return. */
2286 /*! Which match currently on. */
2288};
2289
2290static int complete_agent_search(void *obj, void *arg, void *data, int flags)
2291{
2292 struct agent_complete *search = data;
2293
2294 if (++search->which > search->state) {
2295 return CMP_MATCH;
2296 }
2297 return 0;
2298}
2299
2300static char *complete_agent(const char *word, int state)
2301{
2302 char *ret;
2303 struct agent_pvt *agent;
2304 struct agent_complete search = {
2305 .state = state,
2306 };
2307
2309 complete_agent_search, (char *) word, &search);
2310 if (!agent) {
2311 return NULL;
2312 }
2313 ret = ast_strdup(agent->username);
2314 ao2_ref(agent, -1);
2315 return ret;
2316}
2317
2318static int complete_agent_logoff_search(void *obj, void *arg, void *data, int flags)
2319{
2320 struct agent_pvt *agent = obj;
2321 struct agent_complete *search = data;
2322
2323 if (!agent->logged) {
2324 return 0;
2325 }
2326 if (++search->which > search->state) {
2327 return CMP_MATCH;
2328 }
2329 return 0;
2330}
2331
2332static char *complete_agent_logoff(const char *word, int state)
2333{
2334 char *ret;
2335 struct agent_pvt *agent;
2336 struct agent_complete search = {
2337 .state = state,
2338 };
2339
2341 complete_agent_logoff_search, (char *) word, &search);
2342 if (!agent) {
2343 return NULL;
2344 }
2345 ret = ast_strdup(agent->username);
2346 ao2_ref(agent, -1);
2347 return ret;
2348}
2349
2350static void agent_show_requested(struct ast_cli_args *a, int online_only)
2351{
2352#define FORMAT_HDR "%-8s %-20s %-11s %-30s %s\n"
2353#define FORMAT_ROW "%-8s %-20s %-11s %-30s %s\n"
2354
2355 struct ao2_iterator iter;
2356 struct agent_pvt *agent;
2357 struct ast_str *out = ast_str_alloca(512);
2358 unsigned int agents_total = 0;
2359 unsigned int agents_logged_in = 0;
2360 unsigned int agents_talking = 0;
2361
2362 ast_cli(a->fd, FORMAT_HDR, "Agent-ID", "Name", "State", "Channel", "Talking with");
2363 iter = ao2_iterator_init(agents, 0);
2364 for (; (agent = ao2_iterator_next(&iter)); ao2_ref(agent, -1)) {
2365 struct ast_channel *logged;
2366
2367 ++agents_total;
2368
2369 agent_lock(agent);
2370 logged = agent_lock_logged(agent);
2371 if (logged) {
2372 const char *talking_with;
2373
2374 ++agents_logged_in;
2375
2376 talking_with = pbx_builtin_getvar_helper(logged, "BRIDGEPEER");
2377 if (!ast_strlen_zero(talking_with)) {
2378 ++agents_talking;
2379 } else {
2380 talking_with = "";
2381 }
2382 ast_str_set(&out, 0, FORMAT_ROW, agent->username, agent->cfg->full_name,
2383 ast_devstate_str(agent->devstate), ast_channel_name(logged), talking_with);
2384 ast_channel_unlock(logged);
2385 ast_channel_unref(logged);
2386 } else {
2387 ast_str_set(&out, 0, FORMAT_ROW, agent->username, agent->cfg->full_name,
2388 ast_devstate_str(agent->devstate), "", "");
2389 }
2390 agent_unlock(agent);
2391
2392 if (!online_only || logged) {
2393 ast_cli(a->fd, "%s", ast_str_buffer(out));
2394 }
2395 }
2396 ao2_iterator_destroy(&iter);
2397
2398 ast_cli(a->fd, "\nDefined agents: %u, Logged in: %u, Talking: %u\n",
2399 agents_total, agents_logged_in, agents_talking);
2400
2401#undef FORMAT_HDR
2402#undef FORMAT_ROW
2403}
2404
2405static char *agent_handle_show_online(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2406{
2407 switch (cmd) {
2408 case CLI_INIT:
2409 e->command = "agent show online";
2410 e->usage =
2411 "Usage: agent show online\n"
2412 " Provides summary information for logged in agents.\n";
2413 return NULL;
2414 case CLI_GENERATE:
2415 return NULL;
2416 }
2417
2418 if (a->argc != 3) {
2419 return CLI_SHOWUSAGE;
2420 }
2421
2423
2424 return CLI_SUCCESS;
2425}
2426
2427static char *agent_handle_show_all(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2428{
2429 switch (cmd) {
2430 case CLI_INIT:
2431 e->command = "agent show all";
2432 e->usage =
2433 "Usage: agent show all\n"
2434 " Provides summary information for all agents.\n";
2435 return NULL;
2436 case CLI_GENERATE:
2437 return NULL;
2438 }
2439
2440 if (a->argc != 3) {
2441 return CLI_SHOWUSAGE;
2442 }
2443
2445
2446 return CLI_SUCCESS;
2447}
2448
2449static char *agent_handle_show_specific(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2450{
2451 struct agent_pvt *agent;
2452 struct ast_channel *logged;
2453 struct ast_str *out = ast_str_alloca(4096);
2454
2455 switch (cmd) {
2456 case CLI_INIT:
2457 e->command = "agent show";
2458 e->usage =
2459 "Usage: agent show <agent-id>\n"
2460 " Show information about the <agent-id> agent\n";
2461 return NULL;
2462 case CLI_GENERATE:
2463 if (a->pos == 2) {
2464 return complete_agent(a->word, a->n);
2465 }
2466 return NULL;
2467 }
2468
2469 if (a->argc != 3) {
2470 return CLI_SHOWUSAGE;
2471 }
2472
2473 agent = ao2_find(agents, a->argv[2], OBJ_KEY);
2474 if (!agent) {
2475 ast_cli(a->fd, "Agent '%s' not found\n", a->argv[2]);
2476 return CLI_SUCCESS;
2477 }
2478
2479 agent_lock(agent);
2480 logged = agent_lock_logged(agent);
2481 ast_str_set(&out, 0, "Id: %s\n", agent->username);
2482 ast_str_append(&out, 0, "Name: %s\n", agent->cfg->full_name);
2483 ast_str_append(&out, 0, "Beep: %s\n", agent->cfg->beep_sound);
2484 ast_str_append(&out, 0, "MOH: %s\n", agent->cfg->moh);
2485 ast_str_append(&out, 0, "RecordCalls: %s\n", AST_CLI_YESNO(agent->cfg->record_agent_calls));
2486 ast_str_append(&out, 0, "State: %s\n", ast_devstate_str(agent->devstate));
2487 if (logged) {
2488 const char *talking_with;
2489
2490 ast_str_append(&out, 0, "LoggedInChannel: %s\n", ast_channel_name(logged));
2491 ast_str_append(&out, 0, "LoggedInTime: %ld\n", (long) agent->login_start);
2492 talking_with = pbx_builtin_getvar_helper(logged, "BRIDGEPEER");
2493 if (!ast_strlen_zero(talking_with)) {
2494 ast_str_append(&out, 0, "TalkingWith: %s\n", talking_with);
2495 ast_str_append(&out, 0, "CallStarted: %ld\n", (long) agent->call_start);
2496 }
2497 ast_channel_unlock(logged);
2498 ast_channel_unref(logged);
2499 }
2500 agent_unlock(agent);
2501 ao2_ref(agent, -1);
2502
2503 ast_cli(a->fd, "%s", ast_str_buffer(out));
2504
2505 return CLI_SUCCESS;
2506}
2507
2508static char *agent_handle_logoff_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2509{
2510 switch (cmd) {
2511 case CLI_INIT:
2512 e->command = "agent logoff";
2513 e->usage =
2514 "Usage: agent logoff <agent-id> [soft]\n"
2515 " Sets an agent as no longer logged in.\n"
2516 " If 'soft' is specified, do not hangup existing calls.\n";
2517 return NULL;
2518 case CLI_GENERATE:
2519 if (a->pos == 2) {
2520 return complete_agent_logoff(a->word, a->n);
2521 } else if (a->pos == 3 && a->n == 0
2522 && (ast_strlen_zero(a->word)
2523 || !strncasecmp("soft", a->word, strlen(a->word)))) {
2524 return ast_strdup("soft");
2525 }
2526 return NULL;
2527 }
2528
2529 if (a->argc < 3 || 4 < a->argc) {
2530 return CLI_SHOWUSAGE;
2531 }
2532 if (a->argc == 4 && strcasecmp(a->argv[3], "soft")) {
2533 return CLI_SHOWUSAGE;
2534 }
2535
2536 if (!agent_logoff_request(a->argv[2], a->argc == 4)) {
2537 ast_cli(a->fd, "Logging out %s\n", a->argv[2]);
2538 }
2539
2540 return CLI_SUCCESS;
2541}
2542
2543static struct ast_cli_entry cli_agents[] = {
2544 AST_CLI_DEFINE(agent_handle_show_online, "Show status of online agents"),
2545 AST_CLI_DEFINE(agent_handle_show_all, "Show status of all agents"),
2546 AST_CLI_DEFINE(agent_handle_show_specific, "Show information about an agent"),
2547 AST_CLI_DEFINE(agent_handle_logoff_cmd, "Sets an agent offline"),
2548};
2549
2550static int action_agents(struct mansession *s, const struct message *m)
2551{
2552 const char *id = astman_get_header(m, "ActionID");
2553 char id_text[AST_MAX_BUF];
2554 struct ao2_iterator iter;
2555 struct agent_pvt *agent;
2556 struct ast_str *out = ast_str_alloca(4096);
2557 int num_agents = 0;
2558
2559 if (!ast_strlen_zero(id)) {
2560 snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", id);
2561 } else {
2562 id_text[0] = '\0';
2563 }
2564 astman_send_listack(s, m, "Agents will follow", "start");
2565
2566 iter = ao2_iterator_init(agents, 0);
2567 for (; (agent = ao2_iterator_next(&iter)); ao2_ref(agent, -1)) {
2568 struct ast_channel *logged;
2569
2570 agent_lock(agent);
2571 logged = agent_lock_logged(agent);
2572
2573 /*
2574 * Status Values:
2575 * AGENT_LOGGEDOFF - Agent isn't logged in
2576 * AGENT_IDLE - Agent is logged in, and waiting for call
2577 * AGENT_ONCALL - Agent is logged in, and on a call
2578 * AGENT_UNKNOWN - Don't know anything about agent. Shouldn't ever get this.
2579 */
2580 ast_str_set(&out, 0, "Agent: %s\r\n", agent->username);
2581 ast_str_append(&out, 0, "Name: %s\r\n", agent->cfg->full_name);
2582
2583 if (logged) {
2584 const char *talking_to_chan;
2585 struct ast_str *logged_headers;
2586 RAII_VAR(struct ast_channel_snapshot *, logged_snapshot, ast_channel_snapshot_create(logged), ao2_cleanup);
2587
2588 if (!logged_snapshot
2589 || !(logged_headers =
2590 ast_manager_build_channel_state_string(logged_snapshot))) {
2591 ast_channel_unlock(logged);
2592 ast_channel_unref(logged);
2593 agent_unlock(agent);
2594 continue;
2595 }
2596
2597 talking_to_chan = pbx_builtin_getvar_helper(logged, "BRIDGEPEER");
2598 if (!ast_strlen_zero(talking_to_chan)) {
2599 ast_str_append(&out, 0, "Status: %s\r\n", "AGENT_ONCALL");
2600 ast_str_append(&out, 0, "TalkingToChan: %s\r\n", talking_to_chan);
2601 ast_str_append(&out, 0, "CallStarted: %ld\n", (long) agent->call_start);
2602 } else {
2603 ast_str_append(&out, 0, "Status: %s\r\n", "AGENT_IDLE");
2604 }
2605 ast_str_append(&out, 0, "LoggedInTime: %ld\r\n", (long) agent->login_start);
2606 ast_str_append(&out, 0, "%s", ast_str_buffer(logged_headers));
2607 ast_channel_unlock(logged);
2608 ast_channel_unref(logged);
2609 ast_free(logged_headers);
2610 } else {
2611 ast_str_append(&out, 0, "Status: %s\r\n", "AGENT_LOGGEDOFF");
2612 }
2613
2614 agent_unlock(agent);
2615
2616 astman_append(s, "Event: Agents\r\n"
2617 "%s%s\r\n",
2618 ast_str_buffer(out), id_text);
2619 ++num_agents;
2620 }
2621 ao2_iterator_destroy(&iter);
2622
2623 astman_send_list_complete_start(s, m, "AgentsComplete", num_agents);
2625 return 0;
2626}
2627
2628static int action_agent_logoff(struct mansession *s, const struct message *m)
2629{
2630 const char *agent = astman_get_header(m, "Agent");
2631 const char *soft_s = astman_get_header(m, "Soft"); /* "true" is don't hangup */
2632
2633 if (ast_strlen_zero(agent)) {
2634 astman_send_error(s, m, "No agent specified");
2635 return 0;
2636 }
2637
2638 if (!agent_logoff_request(agent, ast_true(soft_s))) {
2639 astman_send_ack(s, m, "Agent logged out");
2640 } else {
2641 astman_send_error(s, m, "No such agent");
2642 }
2643
2644 return 0;
2645}
2646
2647static int unload_module(void)
2648{
2649 struct ast_bridge *holding;
2650
2651 /* Unregister dialplan applications */
2654
2655 /* Unregister dialplan functions */
2657
2658 /* Unregister manager command */
2659 ast_manager_unregister("Agents");
2660 ast_manager_unregister("AgentLogoff");
2661
2662 /* Unregister CLI commands */
2664
2665 ast_devstate_prov_del("Agent");
2666
2667 /* Destroy agent holding bridge. */
2668 holding = ao2_global_obj_replace(agent_holding, NULL);
2669 if (holding) {
2670 ast_bridge_destroy(holding, 0);
2671 }
2672
2675 agents = NULL;
2676 return 0;
2677}
2678
2679static int load_module(void)
2680{
2681 int res = 0;
2682
2685 if (!agents) {
2687 }
2688
2689 /* Init agent holding bridge v_table. */
2691
2692 /* Setup to provide Agent:agent-id device state. */
2694
2695 /* CLI Commands */
2697
2698 /* Manager commands */
2701
2702 /* Dialplan Functions */
2704
2705 /* Dialplan applications */
2708
2709 if (res) {
2710 ast_log(LOG_ERROR, "Unable to register application. Not loading module.\n");
2711 unload_module();
2713 }
2714
2715 if (load_config()) {
2716 ast_log(LOG_ERROR, "Unable to load config. Not loading module.\n");
2717 unload_module();
2719 }
2720
2722}
2723
2724static int reload(void)
2725{
2726 if (aco_process_config(&cfg_info, 1) == ACO_PROCESS_ERROR) {
2727 /* Just keep the config we already have in place. */
2728 return -1;
2729 }
2730 return 0;
2731}
2732
2733AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Call center agent pool applications",
2734 .support_level = AST_MODULE_SUPPORT_CORE,
2735 .load = load_module,
2736 .unload = unload_module,
2737 .reload = reload,
2738 .load_pri = AST_MODPRI_DEVSTATE_PROVIDER,
static struct ao2_container * agents
static char * agent_handle_logoff_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static int action_agents(struct mansession *s, const struct message *m)
static void * agent_cfg_alloc(const char *name)
static int bridge_agent_hold_heartbeat(struct ast_bridge_channel *bridge_channel, void *hook_pvt)
static char * complete_agent(const char *word, int state)
#define CALLER_SAFETY_TIMEOUT_TIME
static struct ast_custom_function agent_function
static void _agent_unlock(struct agent_pvt *agent, const char *file, const char *function, int line, const char *var)
static AO2_GLOBAL_OBJ_STATIC(cfg_handle)
static char * agent_handle_show_online(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static int agent_pvt_sort_cmp(const void *obj_left, const void *obj_right, int flags)
static int send_colp_to_agent(struct ast_bridge_channel *bridge_channel, struct ast_party_connected_line *connected)
static int caller_joined_bridge(struct ast_bridge_channel *bridge_channel, void *hook_pvt)
#define AST_MAX_BUF
static const char app_agent_login[]
static int agent_request_exec(struct ast_channel *chan, const char *data)
Dialplan AgentRequest application to locate an agent to talk with.
static enum ast_device_state agent_pvt_devstate_get(const char *agent_id)
static void agents_cfg_destructor(void *vdoomed)
#define FORMAT_ROW
static int agent_sweep(void *obj, void *arg, int flags)
static void agent_show_requested(struct ast_cli_args *a, int online_only)
static struct agent_pvt * agent_pvt_new(struct agent_cfg *cfg)
static void destroy_config(void)
static struct aco_type agent_type
static struct ast_bridge_methods bridge_agent_hold_v_table
static int bridge_agent_hold_push(struct ast_bridge *self, struct ast_bridge_channel *bridge_channel, struct ast_bridge_channel *swap)
static char * agent_handle_show_specific(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static int action_agent_logoff(struct mansession *s, const struct message *m)
CONFIG_INFO_STANDARD(cfg_info, cfg_handle, agents_cfg_alloc,.files=ACO_FILES(&agents_conf),.post_apply_config=agents_post_apply_config,)
static void agent_cfg_destructor(void *vdoomed)
static int bridge_agent_hold_deferred_create(void)
static void send_agent_login(struct ast_channel *chan, const char *agent)
static struct aco_type general_type
static struct ast_bridge_channel * agent_bridge_channel_get_lock(struct agent_pvt *agent)
static int send_alert_to_agent(struct ast_bridge_channel *bridge_channel, const char *agent_id)
static void agent_pvt_destructor(void *vdoomed)
static void send_agent_logoff(struct ast_channel *chan, const char *agent, long logintime)
static void bridge_init_agent_hold(void)
static int agent_cfg_sort_cmp(const void *obj_left, const void *obj_right, int flags)
static struct ast_cli_entry cli_agents[]
static const char app_agent_request[]
static void agent_run(struct agent_pvt *agent, struct ast_channel *logged)
static void agent_after_bridge_cb(struct ast_channel *chan, void *data)
static int agent_logoff_request(const char *agent_id, int soft)
static void agent_alert(struct ast_bridge_channel *bridge_channel, const void *payload, size_t payload_size)
static void agent_after_bridge_cb_failed(enum ast_bridge_after_cb_reason reason, void *data)
static int complete_agent_logoff_search(void *obj, void *arg, void *data, int flags)
static char * agent_handle_show_all(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static int caller_safety_timeout(struct ast_bridge_channel *bridge_channel, void *hook_pvt)
static void caller_abort_agent(struct agent_pvt *agent)
#define agent_lock(agent)
Lock the agent.
static void bridge_agent_hold_dissolving(struct ast_bridge *self)
The bridge is being dissolved.
static struct ast_bridge * bridge_agent_hold_new(void)
#define LOGIN_WAIT_TIMEOUT_TIME
agent_override_flags
@ AGENT_FLAG_ACK_CALL
@ AGENT_FLAG_AUTO_LOGOFF
@ AGENT_FLAG_WRAPUP_TIME
@ AGENT_FLAG_DTMF_ACCEPT
AGENT_LOGIN_OPT_FLAGS
@ OPT_SILENT
static void _agent_lock(struct agent_pvt *agent, const char *file, const char *function, int line, const char *var)
static const struct ast_app_option agent_login_opts[128]
static int load_module(void)
static int complete_agent_search(void *obj, void *arg, void *data, int flags)
static void agents_post_apply_config(void)
static int agent_function_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
static int unload_module(void)
static int reload(void)
static int agent_pvt_cmp(void *obj, void *arg, int flags)
static struct ast_channel * agent_lock_logged(struct agent_pvt *agent)
static int agent_mark(void *obj, void *arg, int flags)
static void agent_connect_caller(struct ast_bridge_channel *bridge_channel, struct agent_pvt *agent)
static void agents_mark(void)
static struct aco_type * agent_types[]
static ast_mutex_t agent_holding_lock
static void agents_sweep(void)
static char * complete_agent_logoff(const char *word, int state)
static struct aco_file agents_conf
agent_state
@ AGENT_STATE_LOGGED_OUT
@ AGENT_STATE_CALL_PRESENT
@ AGENT_STATE_PROBATION_WAIT
@ AGENT_STATE_CALL_WAIT_ACK
@ AGENT_STATE_READY_FOR_CALL
@ AGENT_STATE_ON_CALL
@ AGENT_STATE_LOGGING_OUT
@ AGENT_STATE_CALL_WRAPUP
static void bridge_agent_hold_pull(struct ast_bridge *self, struct ast_bridge_channel *bridge_channel)
static int agent_login_exec(struct ast_channel *chan, const char *data)
Dialplan AgentLogin application to log in an agent.
static void agent_login_channel_config(struct agent_pvt *agent, struct ast_channel *chan)
static void * agents_cfg_alloc(void)
static void agent_logout(struct agent_pvt *agent)
#define agent_unlock(agent)
Unlock the agent.
static void agent_devstate_changed(const char *agent_id)
static int load_config(void)
static int bridge_agent_hold_ack(struct ast_bridge_channel *bridge_channel, void *hook_pvt)
static void clear_agent_status(struct ast_bridge_channel *bridge_channel, const void *payload, size_t payload_size)
static void * agent_cfg_find(struct ao2_container *agents, const char *username)
#define FORMAT_HDR
char digit
jack_status_t status
Definition: app_jack.c:149
#define var
Definition: ast_expr2f.c:605
Asterisk main include file. File version handling, generic pbx functions.
#define ast_free(a)
Definition: astmm.h:180
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:241
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
#define ast_log
Definition: astobj2.c:42
#define ao2_t_ref(o, delta, tag)
Definition: astobj2.h:460
#define ao2_iterator_next(iter)
Definition: astobj2.h:1911
#define ao2_link(container, obj)
Add an object to a container.
Definition: astobj2.h:1532
@ CMP_MATCH
Definition: astobj2.h:1027
#define OBJ_KEY
Definition: astobj2.h:1151
#define OBJ_POINTER
Definition: astobj2.h:1150
@ AO2_ALLOC_OPT_LOCK_NOLOCK
Definition: astobj2.h:367
@ AO2_ALLOC_OPT_LOCK_MUTEX
Definition: astobj2.h:363
#define ao2_global_obj_replace_unref(holder, obj)
Replace an ao2 object in the global holder, throwing away any old object.
Definition: astobj2.h:901
#define ao2_callback(c, flags, cb_fn, arg)
ao2_callback() is a generic function that applies cb_fn() to all objects in a container,...
Definition: astobj2.h:1693
@ AO2_LOCK_REQ_MUTEX
Definition: astobj2.h:702
#define ao2_cleanup(obj)
Definition: astobj2.h:1934
#define ao2_callback_data(container, flags, cb_fn, arg, data)
Definition: astobj2.h:1723
#define ao2_global_obj_ref(holder)
Get a reference to the object stored in the global holder.
Definition: astobj2.h:918
#define ao2_find(container, arg, flags)
Definition: astobj2.h:1736
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.
#define ao2_container_alloc_rbtree(ao2_options, container_options, sort_fn, cmp_fn)
Allocate and initialize a red-black tree container.
Definition: astobj2.h:1349
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
#define ao2_global_obj_replace(holder, obj)
Replace an ao2 object in the global holder.
Definition: astobj2.h:878
#define OBJ_PARTIAL_KEY
Definition: astobj2.h:1152
#define ao2_alloc_options(data_size, destructor_fn, options)
Definition: astobj2.h:404
void __ao2_cleanup(void *obj)
Definition: astobj2.c:677
#define ao2_global_obj_release(holder)
Release the ao2 object held in the global holder.
Definition: astobj2.h:859
void ao2_iterator_destroy(struct ao2_iterator *iter)
Destroy a container iterator.
@ OBJ_MULTIPLE
Definition: astobj2.h:1049
@ OBJ_UNLINK
Definition: astobj2.h:1039
#define ao2_alloc(data_size, destructor_fn)
Definition: astobj2.h:409
int __ao2_unlock(void *a, const char *file, const char *func, int line, const char *var)
Unlock an object.
Definition: astobj2.c:288
int __ao2_lock(void *a, enum ao2_lock_req lock_how, const char *file, const char *func, int line, const char *var)
Lock an object.
Definition: astobj2.c:222
@ AO2_CONTAINER_ALLOC_OPT_DUPS_REJECT
Reject objects with duplicate keys in container.
Definition: astobj2.h:1188
@ AO2_CONTAINER_ALLOC_OPT_DUPS_REPLACE
Replace objects with duplicate keys in container.
Definition: astobj2.h:1211
Bridging API.
#define ast_bridge_unlock(bridge)
Unlock the bridge.
Definition: bridge.h:485
int ast_bridge_join(struct ast_bridge *bridge, struct ast_channel *chan, struct ast_channel *swap, struct ast_bridge_features *features, struct ast_bridge_tech_optimizations *tech_args, enum ast_bridge_join_flags flags)
Join a channel to a bridge (blocking)
Definition: bridge.c:1690
int ast_bridge_destroy(struct ast_bridge *bridge, int cause)
Destroy a bridge.
Definition: bridge.c:1009
struct ast_bridge_methods ast_bridge_base_v_table
Bridge base class virtual method table.
Definition: bridge.c:988
@ AST_BRIDGE_CAPABILITY_HOLDING
Definition: bridge.h:90
@ AST_BRIDGE_JOIN_PASS_REFERENCE
Definition: bridge.h:539
int ast_bridge_move(struct ast_bridge *dst_bridge, struct ast_bridge *src_bridge, struct ast_channel *chan, struct ast_channel *swap, int attempt_recovery)
Move a channel from one bridge to another.
Definition: bridge.c:2529
After Bridge Execution API.
ast_bridge_after_cb_reason
Definition: bridge_after.h:37
int ast_bridge_set_after_callback(struct ast_channel *chan, ast_bridge_after_cb callback, ast_bridge_after_cb_failed failed, void *data)
Setup an after bridge callback for when the channel leaves the bridging system.
Definition: bridge_after.c:251
const char * ast_bridge_after_cb_reason_string(enum ast_bridge_after_cb_reason reason)
Get a string representation of an after bridge callback reason.
Definition: bridge_after.c:288
Basic bridge subclass API.
struct ast_bridge * ast_bridge_basic_new(void)
Create a new basic class bridge.
@ AST_BRIDGE_CHANNEL_CB_OPTION_MEDIA
#define ast_bridge_channel_unlock(bridge_channel)
Unlock the bridge_channel.
void ast_bridge_channel_feature_digit(struct ast_bridge_channel *bridge_channel, int digit)
Add a DTMF digit to the collected digits to match against DTMF features.
void ast_bridge_channel_lock_bridge(struct ast_bridge_channel *bridge_channel)
Lock the bridge associated with the bridge channel.
int ast_bridge_channel_write_control_data(struct ast_bridge_channel *bridge_channel, enum ast_control_frame_type control, const void *data, size_t datalen)
Write a control frame into the bridge with data.
#define ast_bridge_channel_lock(bridge_channel)
Lock the bridge_channel.
void ast_bridge_channel_leave_bridge_nolock(struct ast_bridge_channel *bridge_channel, enum bridge_channel_state new_state, int cause)
Set bridge channel state to leave bridge (if not leaving already).
@ BRIDGE_CHANNEL_STATE_END
int ast_bridge_channel_queue_control_data(struct ast_bridge_channel *bridge_channel, enum ast_control_frame_type control, const void *data, size_t datalen)
Queue a control frame onto the bridge channel with data.
void ast_bridge_channel_leave_bridge(struct ast_bridge_channel *bridge_channel, enum bridge_channel_state new_state, int cause)
Set bridge channel state to leave bridge (if not leaving already).
int ast_bridge_channel_queue_callback(struct ast_bridge_channel *bridge_channel, enum ast_bridge_channel_custom_callback_option flags, ast_bridge_custom_callback_fn callback, const void *payload, size_t payload_size)
Queue a bridge action custom callback frame onto the bridge channel.
int ast_bridge_channel_write_callback(struct ast_bridge_channel *bridge_channel, enum ast_bridge_channel_custom_callback_option flags, ast_bridge_custom_callback_fn callback, const void *payload, size_t payload_size)
Write a bridge action custom callback frame into the bridge.
int ast_bridge_features_init(struct ast_bridge_features *features)
Initialize bridge features structure.
Definition: bridge.c:3699
int ast_bridge_interval_hook(struct ast_bridge_features *features, enum ast_bridge_hook_timer_option flags, unsigned int interval, ast_bridge_hook_callback callback, void *hook_pvt, ast_bridge_hook_pvt_destructor destructor, enum ast_bridge_hook_remove_flags remove_flags)
Attach an interval hook to a bridge features structure.
Definition: bridge.c:3398
@ AST_BRIDGE_HOOK_REMOVE_ON_PULL
int ast_bridge_dtmf_hook(struct ast_bridge_features *features, const char *dtmf, ast_bridge_hook_callback callback, void *hook_pvt, ast_bridge_hook_pvt_destructor destructor, enum ast_bridge_hook_remove_flags remove_flags)
Attach a DTMF hook to a bridge features structure.
Definition: bridge.c:3261
@ AST_BRIDGE_BUILTIN_AUTOMIXMON
int ast_bridge_features_do(enum ast_bridge_builtin_feature feature, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
Invoke a built in feature hook now.
Definition: bridge.c:3169
int ast_bridge_join_hook(struct ast_bridge_features *features, ast_bridge_hook_callback callback, void *hook_pvt, ast_bridge_hook_pvt_destructor destructor, enum ast_bridge_hook_remove_flags remove_flags)
Attach a bridge channel join hook to a bridge features structure.
Definition: bridge.c:3354
@ AST_BRIDGE_FLAG_SWAP_INHIBIT_FROM
@ AST_BRIDGE_FLAG_TRANSFER_PROHIBITED
@ AST_BRIDGE_FLAG_MERGE_INHIBIT_TO
@ AST_BRIDGE_FLAG_MERGE_INHIBIT_FROM
void ast_bridge_features_cleanup(struct ast_bridge_features *features)
Clean up the contents of a bridge features structure.
Definition: bridge.c:3732
@ AUTO_MONITOR_START
Private Bridging API.
struct ast_bridge * bridge_register(struct ast_bridge *bridge)
Register the new bridge with the system.
Definition: bridge.c:713
struct ast_bridge * bridge_base_init(struct ast_bridge *self, uint32_t capabilities, unsigned int flags, const char *creator, const char *name, const char *id)
Initialize the base class of the bridge.
Definition: bridge.c:783
struct ast_bridge * bridge_alloc(size_t size, const struct ast_bridge_methods *v_table)
Definition: bridge.c:747
int ast_channel_set_bridge_role_option(struct ast_channel *channel, const char *role_name, const char *option, const char *value)
Set a role option on a channel.
Definition: bridge_roles.c:375
void ast_channel_remove_bridge_role(struct ast_channel *chan, const char *role_name)
Removes a bridge role from a channel.
Definition: bridge_roles.c:332
int ast_channel_add_bridge_role(struct ast_channel *chan, const char *role_name)
Adds a bridge role to a channel.
Definition: bridge_roles.c:313
int ast_bridge_channel_establish_roles(struct ast_bridge_channel *bridge_channel)
Clone the roles from a bridge_channel's attached ast_channel onto the bridge_channel's role list.
Definition: bridge_roles.c:443
void ast_bridge_channel_clear_roles(struct ast_bridge_channel *bridge_channel)
Clear all roles from a bridge_channel's role list.
Definition: bridge_roles.c:491
Internal Asterisk hangup causes.
#define AST_CAUSE_NORMAL_CLEARING
Definition: causes.h:106
enum cc_state state
Definition: ccss.c:399
General Asterisk PBX channel definitions.
const char * ast_channel_name(const struct ast_channel *chan)
void ast_party_connected_line_free(struct ast_party_connected_line *doomed)
Destroy the connected line information contents.
Definition: channel.c:2040
#define ast_channel_lock(chan)
Definition: channel.h:2972
void ast_party_connected_line_copy(struct ast_party_connected_line *dest, const struct ast_party_connected_line *src)
Copy the source connected line information to the destination connected line.
Definition: channel.c:1999
struct ast_flags * ast_channel_flags(struct ast_channel *chan)
#define ast_channel_ref(c)
Increase channel reference count.
Definition: channel.h:2997
struct ast_party_connected_line * ast_channel_connected(struct ast_channel *chan)
@ AST_FLAG_ZOMBIE
Definition: channel.h:1007
int ast_check_hangup_locked(struct ast_channel *chan)
Definition: channel.c:459
void ast_channel_update_connected_line(struct ast_channel *chan, const struct ast_party_connected_line *connected, const struct ast_set_party_connected_line *update)
Indicate that the connected line information has changed.
Definition: channel.c:9107
int ast_check_hangup(struct ast_channel *chan)
Check to see if a channel is needing hang up.
Definition: channel.c:445
@ AST_SOFTHANGUP_ASYNCGOTO
Definition: channel.h:1146
@ AST_SOFTHANGUP_EXPLICIT
Definition: channel.h:1168
int ast_softhangup(struct ast_channel *chan, int cause)
Softly hangup up a channel.
Definition: channel.c:2441
int ast_connected_line_build_data(unsigned char *data, size_t datalen, const struct ast_party_connected_line *connected, const struct ast_set_party_connected_line *update)
Build the connected line information data frame.
Definition: channel.c:8711
#define ast_channel_unref(c)
Decrease channel reference count.
Definition: channel.h:3008
struct ast_format * ast_channel_writeformat(struct ast_channel *chan)
void ast_party_connected_line_init(struct ast_party_connected_line *init)
Initialize the given connected line structure.
Definition: channel.c:1990
struct ast_bridge_channel * ast_channel_get_bridge_channel(struct ast_channel *chan)
Get a reference to the channel's bridge pointer.
Definition: channel.c:10596
void ast_connected_line_copy_from_caller(struct ast_party_connected_line *dest, const struct ast_party_caller *src)
Copy the caller information to the connected line information.
Definition: channel.c:8307
int ast_channel_softhangup_internal_flag(struct ast_channel *chan)
struct ast_party_caller * ast_channel_caller(struct ast_channel *chan)
void ast_channel_hangupcause_set(struct ast_channel *chan, int value)
int ast_answer(struct ast_channel *chan)
Answer a channel.
Definition: channel.c:2776
int ast_indicate(struct ast_channel *chan, int condition)
Indicates condition of channel.
Definition: channel.c:4243
#define ast_channel_unlock(chan)
Definition: channel.h:2973
struct ast_format * ast_channel_readformat(struct ast_channel *chan)
ast_channel_state
ast_channel states
Definition: channelstate.h:35
@ AST_STATE_UP
Definition: channelstate.h:42
Standard Command Line Interface.
#define CLI_SHOWUSAGE
Definition: cli.h:45
#define AST_CLI_YESNO(x)
Return Yes or No depending on the argument.
Definition: cli.h:71
#define CLI_SUCCESS
Definition: cli.h:44
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
Definition: clicompat.c:30
#define AST_CLI_DEFINE(fn, txt,...)
Definition: cli.h:197
void ast_cli(int fd, const char *fmt,...)
Definition: clicompat.c:6
@ CLI_INIT
Definition: cli.h:152
@ CLI_GENERATE
Definition: cli.h:153
#define ast_cli_register_multiple(e, len)
Register multiple commands.
Definition: cli.h:265
static void update(int code_size, int y, int wi, int fi, int dq, int sr, int dqsez, struct g726_state *state_ptr)
Definition: codec_g726.c:367
short word
Configuration option-handling.
@ ACO_EXACT
void aco_info_destroy(struct aco_info *info)
Destroy an initialized aco_info struct.
@ ACO_PROCESS_ERROR
Their was an error and no changes were applied.
#define STRFLDSET(type,...)
Convert a struct and a list of stringfield fields to an argument list of field offsets.
int aco_info_init(struct aco_info *info)
Initialize an aco_info structure.
#define FLDSET(type,...)
Convert a struct and list of fields to an argument list of field offsets.
#define aco_option_register(info, name, matchtype, types, default_val, opt_type, flags,...)
Register a config option.
#define ACO_FILES(...)
@ OPT_UINT_T
Type for default option handler for unsigned integers.
@ OPT_BOOL_T
Type for default option handler for bools (ast_true/ast_false)
@ OPT_STRINGFIELD_T
Type for default option handler for stringfields.
@ ACO_ITEM
@ ACO_GLOBAL
@ ACO_WHITELIST_EXACT
@ ACO_BLACKLIST_EXACT
enum aco_process_status aco_process_config(struct aco_info *info, int reload)
Process a config info via the options registered with an aco_info.
#define ACO_TYPES(...)
A helper macro to ensure that aco_info types always have a sentinel.
int ast_devstate_prov_del(const char *label)
Remove device state provider.
Definition: devicestate.c:421
@ AST_DEVSTATE_CACHABLE
Definition: devicestate.h:70
const char * ast_devstate_str(enum ast_device_state devstate) attribute_pure
Convert device state to text string that is easier to parse.
Definition: devicestate.c:258
int ast_devstate_changed(enum ast_device_state state, enum ast_devstate_cache cachable, const char *fmt,...)
Tells Asterisk the State for Device is changed.
Definition: devicestate.c:513
int ast_devstate_prov_add(const char *label, ast_devstate_prov_cb_type callback)
Add device state provider.
Definition: devicestate.c:394
ast_device_state
Device States.
Definition: devicestate.h:52
@ AST_DEVICE_INUSE
Definition: devicestate.h:55
@ AST_DEVICE_UNKNOWN
Definition: devicestate.h:53
@ AST_DEVICE_INVALID
Definition: devicestate.h:57
@ AST_DEVICE_NOT_INUSE
Definition: devicestate.h:54
@ AST_DEVICE_UNAVAILABLE
Definition: devicestate.h:58
char connected
Definition: eagi_proxy.c:82
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
int ast_stopstream(struct ast_channel *c)
Stops a stream.
Definition: file.c:223
int ast_stream_and_wait(struct ast_channel *chan, const char *file, const char *digits)
stream file until digit If the file name is non-empty, try to play it.
Definition: file.c:1912
#define AST_DIGIT_NONE
Definition: file.h:47
#define AST_DIGIT_ANY
Definition: file.h:48
const char * ast_format_get_name(const struct ast_format *format)
Get the name associated with a format.
Definition: format.c:334
static const char name[]
Definition: format_mp3.c:68
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
void astman_send_listack(struct mansession *s, const struct message *m, char *msg, char *listflag)
Send ack in manager transaction to begin a list.
Definition: manager.c:2024
void astman_send_error(struct mansession *s, const struct message *m, char *error)
Send error in manager transaction.
Definition: manager.c:1982
void astman_send_list_complete_start(struct mansession *s, const struct message *m, const char *event_name, int count)
Start the list complete event.
Definition: manager.c:2060
void astman_send_ack(struct mansession *s, const struct message *m, char *msg)
Send ack in manager transaction.
Definition: manager.c:2014
const char * astman_get_header(const struct message *m, char *var)
Get header from manager transaction.
Definition: manager.c:1643
void astman_send_list_complete_end(struct mansession *s)
End the list complete event.
Definition: manager.c:2068
void astman_append(struct mansession *s, const char *fmt,...)
Definition: manager.c:1903
int ast_manager_unregister(const char *action)
Unregister a registered manager command.
Definition: manager.c:7695
struct stasis_message_type * ast_channel_agent_logoff_type(void)
Message type for agent logoff on a channel.
struct ast_channel_snapshot * ast_channel_snapshot_create(struct ast_channel *chan)
Generate a snapshot of the channel state. This is an ao2 object, so ao2_cleanup() to deallocate.
struct stasis_message_type * ast_channel_agent_login_type(void)
Message type for agent login on a channel.
void ast_channel_publish_blob(struct ast_channel *chan, struct stasis_message_type *type, struct ast_json *blob)
Publish a channel blob message.
#define bc
Application convenience functions, designed to give consistent look and feel to Asterisk apps.
#define AST_APP_ARG(name)
Define an application argument.
#define END_OPTIONS
#define AST_APP_OPTIONS(holder, options...)
Declares an array of options for an application.
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application's arguments.
#define BEGIN_OPTIONS
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the 'standard' argument separation process for an application.
#define AST_APP_OPTION(option, flagno)
Declares an application option that does not accept an argument.
#define AST_NONSTANDARD_APP_ARGS(args, parse, sep)
Performs the 'nonstandard' argument separation process for an application.
int ast_app_parse_options(const struct ast_app_option *options, struct ast_flags *flags, char **args, char *optstr)
Parses a string containing application options and sets flags/arguments.
Definition: main/app.c:3066
#define AST_FEATURE_MAX_LEN
@ AST_CONTROL_ANSWER
@ AST_CONTROL_RINGING
@ AST_CONTROL_CONNECTED_LINE
#define ast_debug(level,...)
Log a DEBUG message.
#define LOG_ERROR
#define ast_verb(level,...)
#define LOG_NOTICE
#define LOG_WARNING
void ast_json_unref(struct ast_json *value)
Decrease refcount on value. If refcount reaches zero, value is freed.
Definition: json.c:73
struct ast_json * ast_json_pack(char const *format,...)
Helper for creating complex JSON values.
Definition: json.c:612
AST_JSON_INT_T ast_json_int_t
Primarily used to cast when packing to an "I" type.
Definition: json.h:87
#define ast_mutex_unlock(a)
Definition: lock.h:197
#define ast_mutex_lock(a)
Definition: lock.h:196
#define AST_MUTEX_DEFINE_STATIC(mutex)
Definition: lock.h:527
struct ast_str * ast_manager_build_channel_state_string(const struct ast_channel_snapshot *snapshot)
Generate the AMI message body from a channel snapshot.
#define ast_manager_register_xml(action, authority, func)
Register a manager callback using XML documentation to describe the manager.
Definition: manager.h:192
#define EVENT_FLAG_AGENT
Definition: manager.h:80
Asterisk module definitions.
@ AST_MODFLAG_LOAD_ORDER
Definition: module.h:331
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition: module.h:557
@ AST_MODPRI_DEVSTATE_PROVIDER
Definition: module.h:343
@ AST_MODULE_SUPPORT_CORE
Definition: module.h:121
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
int ast_unregister_application(const char *app)
Unregister an application.
Definition: pbx_app.c:392
@ AST_MODULE_LOAD_SUCCESS
Definition: module.h:70
@ AST_MODULE_LOAD_DECLINE
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
#define ast_register_application_xml(app, execute)
Register an application using XML documentation.
Definition: module.h:640
Core PBX routines and definitions.
const char * pbx_builtin_getvar_helper(struct ast_channel *chan, const char *name)
Return a pointer to the value of the corresponding channel variable.
int pbx_builtin_setvar_helper(struct ast_channel *chan, const char *name, const char *value)
Add a variable to the channel variable stack, removing the most recently set value for the same name.
#define ast_custom_function_register(acf)
Register a custom function.
Definition: pbx.h:1562
int ast_custom_function_unregister(struct ast_custom_function *acf)
Unregister a custom function.
static corosync_cfg_handle_t cfg_handle
Definition: res_corosync.c:284
#define NULL
Definition: resample.c:96
#define AST_DECLARE_STRING_FIELDS(field_list)
Declare the fields needed in a structure.
Definition: stringfields.h:341
#define AST_STRING_FIELD(name)
Declare a string field.
Definition: stringfields.h:303
#define ast_string_field_set(x, field, data)
Set a field to a simple string value.
Definition: stringfields.h:521
#define ast_string_field_init(x, size)
Initialize a field pool and fields.
Definition: stringfields.h:359
#define ast_string_field_free_memory(x)
free all memory - to be called before destroying the object
Definition: stringfields.h:374
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
Definition: strings.h:1139
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:761
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is "true"....
Definition: utils.c:2235
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:65
#define ast_str_alloca(init_len)
Definition: strings.h:848
int ast_str_set(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Set a dynamic string using variable arguments.
Definition: strings.h:1113
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:425
The representation of a single configuration file to be processed.
const char * filename
Type information about a category-level configurable object.
enum aco_type_t type
const ast_string_field username
const ast_string_field moh
const ast_string_field full_name
const ast_string_field dtmf_accept
unsigned int auto_logoff
Number of seconds for agent to ack a call before being logged off.
unsigned int wrapup_time
Time after a call in ms before the agent can get a new call.
int ack_call
TRUE if agent needs to ack a call to accept it.
const ast_string_field beep_sound
int record_agent_calls
Structure representing an agent.
unsigned int override_auto_logoff
enum ast_device_state devstate
unsigned int dead
TRUE if the agent is no longer configured and is being destroyed.
struct ast_party_connected_line waiting_colp
const ast_string_field override_dtmf_accept
enum agent_state state
unsigned int the_mark
time_t probation_start
struct timeval last_disconnect
struct timeval ack_time
const ast_string_field username
struct ast_bridge * caller_bridge
struct ast_channel * logged
unsigned int override_ack_call
time_t call_start
unsigned int override_wrapup_time
unsigned int flags
unsigned int deferred_logoff
time_t login_start
struct agent_cfg * cfg
struct ao2_container * agents
Generic container type.
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
Definition: astobj2.h:1821
Structure that contains information regarding a channel in a bridge.
struct ast_bridge * bridge
Bridge this channel is participating in.
struct ast_bridge_features * features
struct ast_channel * chan
Structure that contains features information.
Bridge virtual methods table definition.
Definition: bridge.h:261
ast_bridge_dissolving_fn dissolving
Definition: bridge.h:267
ast_bridge_push_channel_fn push
Definition: bridge.h:269
const char * name
Definition: bridge.h:263
ast_bridge_pull_channel_fn pull
Definition: bridge.h:271
Structure that contains information about a bridge.
Definition: bridge.h:353
Structure representing a snapshot of channel state.
Main Channel structure associated with a channel.
struct ast_bridge_channel * bridge_channel
const char * data
descriptor for a cli entry.
Definition: cli.h:171
char * command
Definition: cli.h:186
const char * usage
Definition: cli.h:177
Data structure associated with a custom dialplan function.
Definition: pbx.h:118
const char * name
Definition: pbx.h:119
Structure used to handle boolean flags.
Definition: utils.h:217
Abstract JSON element (object, array, string, int, ...).
Connected Line/Party information.
Definition: channel.h:458
Indicate what information in ast_party_connected_line should be set.
Definition: channel.h:491
Support for dynamic strings.
Definition: strings.h:623
In case you didn't read that giant block of text above the mansession_session struct,...
Definition: manager.c:323
static struct aco_type item
Definition: test_config.c:1463
const char * args
static struct test_options options
static struct test_val a
int64_t ast_tvdiff_ms(struct timeval end, struct timeval start)
Computes the difference (in milliseconds) between two struct timeval instances.
Definition: time.h:107
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition: time.h:159
FILE * out
Definition: utils/frame.c:33
#define ast_test_flag(p, flag)
Definition: utils.h:63
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition: utils.h:978
#define ast_assert(a)
Definition: utils.h:776
#define ast_clear_flag(p, flag)
Definition: utils.h:77
#define ast_set_flag(p, flag)
Definition: utils.h:70
#define ARRAY_LEN(a)
Definition: utils.h:703
#define ast_copy_flags(dest, src, flagz)
Definition: utils.h:84
#define AST_FLAGS_ALL
Definition: utils.h:214