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