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