Asterisk - The Open Source Telephony Project  21.4.1
Data Structures | Macros | Enumerations | Functions | Variables
app_agent_pool.c File Reference

Call center agent pool. More...

#include "asterisk.h"
#include "asterisk/cli.h"
#include "asterisk/app.h"
#include "asterisk/pbx.h"
#include "asterisk/module.h"
#include "asterisk/channel.h"
#include "asterisk/bridge.h"
#include "asterisk/bridge_internal.h"
#include "asterisk/bridge_basic.h"
#include "asterisk/bridge_after.h"
#include "asterisk/config_options.h"
#include "asterisk/features_config.h"
#include "asterisk/astobj2.h"
#include "asterisk/stringfields.h"
#include "asterisk/stasis_channels.h"
#include "asterisk/causes.h"

Go to the source code of this file.

Data Structures

struct  agent_cfg
 
struct  agent_complete
 
struct  agent_pvt
 Structure representing an agent. More...
 
struct  agents_cfg
 

Macros

#define agent_lock(agent)   _agent_lock(agent, __FILE__, __PRETTY_FUNCTION__, __LINE__, #agent)
 Lock the agent. More...
 
#define agent_unlock(agent)   _agent_unlock(agent, __FILE__, __PRETTY_FUNCTION__, __LINE__, #agent)
 Unlock the agent. More...
 
#define AST_MAX_BUF   256
 
#define CALLER_SAFETY_TIMEOUT_TIME   (2 * 60 * 1000)
 
#define FORMAT_HDR   "%-8s %-20s %-11s %-30s %s\n"
 
#define FORMAT_ROW   "%-8s %-20s %-11s %-30s %s\n"
 
#define LOGIN_WAIT_TIMEOUT_TIME   5
 

Enumerations

enum  AGENT_LOGIN_OPT_FLAGS { OPT_SILENT = (1 << 0) }
 
enum  agent_override_flags { AGENT_FLAG_ACK_CALL = (1 << 0), AGENT_FLAG_DTMF_ACCEPT = (1 << 1), AGENT_FLAG_AUTO_LOGOFF = (1 << 2), AGENT_FLAG_WRAPUP_TIME = (1 << 3) }
 
enum  agent_state {
  AGENT_STATE_LOGGED_OUT, AGENT_STATE_PROBATION_WAIT, AGENT_STATE_READY_FOR_CALL, AGENT_STATE_CALL_PRESENT,
  AGENT_STATE_CALL_WAIT_ACK, AGENT_STATE_ON_CALL, AGENT_STATE_CALL_WRAPUP, AGENT_STATE_LOGGING_OUT
}
 

Functions

static void __reg_module (void)
 
static void __unreg_module (void)
 
static void _agent_lock (struct agent_pvt *agent, const char *file, const char *function, int line, const char *var)
 
static void _agent_unlock (struct agent_pvt *agent, const char *file, const char *function, int line, const char *var)
 
static int action_agent_logoff (struct mansession *s, const struct message *m)
 
static int action_agents (struct mansession *s, const struct message *m)
 
static void agent_after_bridge_cb (struct ast_channel *chan, void *data)
 
static void agent_after_bridge_cb_failed (enum ast_bridge_after_cb_reason reason, void *data)
 
static void agent_alert (struct ast_bridge_channel *bridge_channel, const void *payload, size_t payload_size)
 
static struct ast_bridge_channelagent_bridge_channel_get_lock (struct agent_pvt *agent)
 
static void * agent_cfg_alloc (const char *name)
 
static void agent_cfg_destructor (void *vdoomed)
 
static void * agent_cfg_find (struct ao2_container *agents, const char *username)
 
static int agent_cfg_sort_cmp (const void *obj_left, const void *obj_right, int flags)
 
static void agent_connect_caller (struct ast_bridge_channel *bridge_channel, struct agent_pvt *agent)
 
static void agent_devstate_changed (const char *agent_id)
 
static int agent_function_read (struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
 
static char * agent_handle_logoff_cmd (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 
static char * agent_handle_show_all (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 
static char * agent_handle_show_online (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 
static char * agent_handle_show_specific (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 
static struct ast_channelagent_lock_logged (struct agent_pvt *agent)
 
static void agent_login_channel_config (struct agent_pvt *agent, struct ast_channel *chan)
 
static int agent_login_exec (struct ast_channel *chan, const char *data)
 Dialplan AgentLogin application to log in an agent. More...
 
static int agent_logoff_request (const char *agent_id, int soft)
 
static void agent_logout (struct agent_pvt *agent)
 
static int agent_mark (void *obj, void *arg, int flags)
 
static int agent_pvt_cmp (void *obj, void *arg, int flags)
 
static void agent_pvt_destructor (void *vdoomed)
 
static enum ast_device_state agent_pvt_devstate_get (const char *agent_id)
 
static struct agent_pvtagent_pvt_new (struct agent_cfg *cfg)
 
static int agent_pvt_sort_cmp (const void *obj_left, const void *obj_right, int flags)
 
static int agent_request_exec (struct ast_channel *chan, const char *data)
 Dialplan AgentRequest application to locate an agent to talk with. More...
 
static void agent_run (struct agent_pvt *agent, struct ast_channel *logged)
 
static void agent_show_requested (struct ast_cli_args *a, int online_only)
 
static int agent_sweep (void *obj, void *arg, int flags)
 
static void * agents_cfg_alloc (void)
 
static void agents_cfg_destructor (void *vdoomed)
 
static void agents_mark (void)
 
static void agents_post_apply_config (void)
 
static void agents_sweep (void)
 
static AO2_GLOBAL_OBJ_STATIC (cfg_handle)
 
static AO2_GLOBAL_OBJ_STATIC (agent_holding)
 
struct ast_moduleAST_MODULE_SELF_SYM (void)
 
static int bridge_agent_hold_ack (struct ast_bridge_channel *bridge_channel, void *hook_pvt)
 
static int bridge_agent_hold_deferred_create (void)
 
static void bridge_agent_hold_dissolving (struct ast_bridge *self)
 The bridge is being dissolved. More...
 
static int bridge_agent_hold_heartbeat (struct ast_bridge_channel *bridge_channel, void *hook_pvt)
 
static struct ast_bridgebridge_agent_hold_new (void)
 
static void bridge_agent_hold_pull (struct ast_bridge *self, struct ast_bridge_channel *bridge_channel)
 
static int bridge_agent_hold_push (struct ast_bridge *self, struct ast_bridge_channel *bridge_channel, struct ast_bridge_channel *swap)
 
static void bridge_init_agent_hold (void)
 
static void caller_abort_agent (struct agent_pvt *agent)
 
static int caller_joined_bridge (struct ast_bridge_channel *bridge_channel, void *hook_pvt)
 
static int caller_safety_timeout (struct ast_bridge_channel *bridge_channel, void *hook_pvt)
 
static void clear_agent_status (struct ast_bridge_channel *bridge_channel, const void *payload, size_t payload_size)
 
static char * complete_agent (const char *word, int state)
 
static char * complete_agent_logoff (const char *word, int state)
 
static int complete_agent_logoff_search (void *obj, void *arg, void *data, int flags)
 
static int complete_agent_search (void *obj, void *arg, void *data, int flags)
 
 CONFIG_INFO_STANDARD (cfg_info, cfg_handle, agents_cfg_alloc,.files=ACO_FILES(&agents_conf),.post_apply_config=agents_post_apply_config,)
 
static void destroy_config (void)
 
static int load_config (void)
 
static int load_module (void)
 
static int reload (void)
 
static void send_agent_login (struct ast_channel *chan, const char *agent)
 
static void send_agent_logoff (struct ast_channel *chan, const char *agent, long logintime)
 
static int send_alert_to_agent (struct ast_bridge_channel *bridge_channel, const char *agent_id)
 
static int send_colp_to_agent (struct ast_bridge_channel *bridge_channel, struct ast_party_connected_line *connected)
 
static int unload_module (void)
 

Variables

static struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "Call center agent pool applications" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "da6642af068ee5e6490c5b1d2cc1d238" , .support_level = AST_MODULE_SUPPORT_CORE, .load = load_module, .unload = unload_module, .reload = reload, .load_pri = AST_MODPRI_DEVSTATE_PROVIDER, }
 
static struct ast_custom_function agent_function
 
static ast_mutex_t agent_holding_lock = { PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP , NULL, {1, 0} }
 
static const struct ast_app_option agent_login_opts [128] = { [ 's' ] = { .flag = OPT_SILENT }, }
 
static struct aco_type agent_type
 
static struct aco_typeagent_types [] = ACO_TYPES(&agent_type)
 
static struct ao2_containeragents
 
static struct aco_file agents_conf
 
static const char app_agent_login [] = "AgentLogin"
 
static const char app_agent_request [] = "AgentRequest"
 
static const struct ast_module_infoast_module_info = &__mod_info
 
static struct ast_bridge_methods bridge_agent_hold_v_table
 
static struct ast_cli_entry cli_agents []
 
static struct aco_type general_type
 

Detailed Description

Call center agent pool.

Author
Richard Mudgett rmudg.nosp@m.ett@.nosp@m.digiu.nosp@m.m.co.nosp@m.m

See Also:

Definition in file app_agent_pool.c.

Macro Definition Documentation

#define agent_lock (   agent)    _agent_lock(agent, __FILE__, __PRETTY_FUNCTION__, __LINE__, #agent)

Lock the agent.

Parameters
agentAgent to lock

Definition at line 656 of file app_agent_pool.c.

Referenced by agent_login_exec(), agent_request_exec(), and bridge_agent_hold_push().

#define agent_unlock (   agent)    _agent_unlock(agent, __FILE__, __PRETTY_FUNCTION__, __LINE__, #agent)

Unlock the agent.

Parameters
agentAgent to unlock

Definition at line 667 of file app_agent_pool.c.

Referenced by agent_login_exec(), agent_request_exec(), and bridge_agent_hold_push().

#define CALLER_SAFETY_TIMEOUT_TIME   (2 * 60 * 1000)

Maximum wait time (in ms) for the custom_beep file to play announcing the caller.

Definition at line 345 of file app_agent_pool.c.

Referenced by agent_request_exec().

#define LOGIN_WAIT_TIMEOUT_TIME   5

Number of seconds to wait for local channel optimizations to complete.

Definition at line 348 of file app_agent_pool.c.

Enumeration Type Documentation

Agent config option override flags.

Definition at line 586 of file app_agent_pool.c.

586  {
587  AGENT_FLAG_ACK_CALL = (1 << 0),
588  AGENT_FLAG_DTMF_ACCEPT = (1 << 1),
589  AGENT_FLAG_AUTO_LOGOFF = (1 << 2),
590  AGENT_FLAG_WRAPUP_TIME = (1 << 3),
591 };
Enumerator
AGENT_STATE_LOGGED_OUT 

The agent is defined but an agent is not present.

AGENT_STATE_PROBATION_WAIT 

Forced initial login wait to allow any local channel optimizations to happen.

AGENT_STATE_READY_FOR_CALL 

The agent is ready for a call.

AGENT_STATE_CALL_PRESENT 

The agent has a call waiting to connect.

AGENT_STATE_CALL_WAIT_ACK 

The agent needs to ack the call.

AGENT_STATE_ON_CALL 

The agent is connected with a call.

AGENT_STATE_CALL_WRAPUP 

The agent is resting between calls.

AGENT_STATE_LOGGING_OUT 

The agent is being kicked out.

Definition at line 566 of file app_agent_pool.c.

566  {
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 };

Function Documentation

static int agent_login_exec ( struct ast_channel chan,
const char *  data 
)
static

Dialplan AgentLogin application to log in an agent.

Parameters
chanChannel attempting to login as an agent.
dataApplication parameters
Return values
0To continue in dialplan.
-1To hangup.

Definition at line 2090 of file app_agent_pool.c.

References agent_lock, agent_unlock, ast_answer(), AST_APP_ARG, ast_app_parse_options(), ast_channel_ref, AST_DECLARE_APP_ARGS, AST_DIGIT_NONE, ast_format_get_name(), AST_STANDARD_APP_ARGS, AST_STATE_UP, ast_strdupa, ast_stream_and_wait(), ast_tvnow(), agent_pvt::deferred_logoff, agent_pvt::last_disconnect, agent_pvt::logged, agent_pvt::login_start, OBJ_KEY, pbx_builtin_setvar_helper(), RAII_VAR, and agent_pvt::username.

2091 {
2092  char *parse;
2093  struct ast_flags opts;
2094  AST_DECLARE_APP_ARGS(args,
2095  AST_APP_ARG(agent_id);
2096  AST_APP_ARG(options);
2097  AST_APP_ARG(other); /* Any remaining unused arguments */
2098  );
2099 
2100  RAII_VAR(struct agent_pvt *, agent, NULL, ao2_cleanup);
2101 
2102  if (bridge_agent_hold_deferred_create()) {
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);
2111  AST_STANDARD_APP_ARGS(args, parse);
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,
2152  ast_format_get_name(ast_channel_readformat(chan)),
2153  ast_format_get_name(ast_channel_writeformat(chan)));
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 }
#define agent_unlock(agent)
Unlock the agent.
#define OBJ_KEY
Definition: astobj2.h:1151
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the 'standard' argument separation process for an application.
ast_channel_state
ast_channel states
Definition: channelstate.h:35
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition: time.h:159
const char * ast_format_get_name(const struct ast_format *format)
Get the name associated with a format.
Definition: format.c:334
#define agent_lock(agent)
Lock the agent.
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
int ast_app_parse_options(const struct ast_app_option *options, struct ast_flags *flags, char **args, char *optstr)
Parses a string containing application options and sets flags/arguments.
Definition: main/app.c:3066
Structure representing an agent.
#define AST_DIGIT_NONE
Definition: file.h:47
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:1878
Structure used to handle boolean flags.
Definition: utils.h:199
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_channel_ref(c)
Increase channel reference count.
Definition: channel.h:2947
int ast_answer(struct ast_channel *chan)
Answer a channel.
Definition: channel.c:2805
#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:941
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application's arguments.
#define AST_APP_ARG(name)
Define an application argument.
static struct ao2_container * agents
static int agent_request_exec ( struct ast_channel chan,
const char *  data 
)
static

Dialplan AgentRequest application to locate an agent to talk with.

Parameters
chanChannel wanting to talk with an agent.
dataApplication parameters
Return values
0To continue in dialplan.
-1To hangup.

Definition at line 1869 of file app_agent_pool.c.

References __ao2_cleanup(), agent_lock, AGENT_STATE_CALL_PRESENT, AGENT_STATE_LOGGED_OUT, AGENT_STATE_LOGGING_OUT, AGENT_STATE_READY_FOR_CALL, agent_unlock, ao2_ref, AST_APP_ARG, ast_bridge_basic_new(), ast_bridge_channel_unlock, ast_bridge_destroy(), ast_bridge_features_cleanup(), ast_bridge_features_init(), AST_BRIDGE_HOOK_REMOVE_ON_PULL, ast_bridge_interval_hook(), ast_bridge_join(), ast_bridge_join_hook(), AST_BRIDGE_JOIN_PASS_REFERENCE, ast_check_hangup(), ast_connected_line_copy_from_caller(), AST_DECLARE_APP_ARGS, AST_DEVICE_INUSE, AST_FLAG_ZOMBIE, ast_party_connected_line_free(), ast_party_connected_line_init(), AST_SOFTHANGUP_ASYNCGOTO, AST_STANDARD_APP_ARGS, ast_strdupa, agent_pvt::caller_bridge, CALLER_SAFETY_TIMEOUT_TIME, agent_pvt::devstate, OBJ_KEY, pbx_builtin_getvar_helper(), pbx_builtin_setvar_helper(), RAII_VAR, agent_pvt::state, and agent_pvt::username.

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;
1876  struct ast_party_connected_line connected;
1877  AST_DECLARE_APP_ARGS(args,
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 
1884  if (bridge_agent_hold_deferred_create()) {
1885  return -1;
1886  }
1887 
1888  parse = ast_strdupa(data);
1889  AST_STANDARD_APP_ARGS(args, parse);
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,
1911  caller_safety_timeout, agent, __ao2_cleanup, AST_BRIDGE_HOOK_REMOVE_ON_PULL)) {
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;
1945  agent->state = AGENT_STATE_CALL_PRESENT;
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);
1957  agent_devstate_changed(agent->username);
1958 
1959  /* Get COLP for agent. */
1960  ast_party_connected_line_init(&connected);
1961  ast_channel_lock(chan);
1962  ast_connected_line_copy_from_caller(&connected, ast_channel_caller(chan));
1963  ast_channel_unlock(chan);
1964 
1965  logged = agent_bridge_channel_get_lock(agent);
1966  if (!logged) {
1967  ast_party_connected_line_free(&connected);
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);
1979  ast_party_connected_line_free(&connected);
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);
1992  if (ast_channel_softhangup_internal_flag(chan) & AST_SOFTHANGUP_ASYNCGOTO) {
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)
2002  || ast_test_flag(ast_channel_flags(chan), AST_FLAG_ZOMBIE)
2003  || ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENT_STATUS"));
2004  }
2005  ast_channel_unlock(chan);
2006 
2007  return res ? -1 : 0;
2008 }
void ast_party_connected_line_init(struct ast_party_connected_line *init)
Initialize the given connected line structure.
Definition: channel.c:2022
void ast_bridge_features_cleanup(struct ast_bridge_features *features)
Clean up the contents of a bridge features structure.
Definition: bridge.c:3653
#define CALLER_SAFETY_TIMEOUT_TIME
#define agent_unlock(agent)
Unlock the agent.
Structure that contains features information.
#define OBJ_KEY
Definition: astobj2.h:1151
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
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the 'standard' argument separation process for an application.
int ast_bridge_features_init(struct ast_bridge_features *features)
Initialize bridge features structure.
Definition: bridge.c:3620
void __ao2_cleanup(void *obj)
Definition: astobj2.c:677
int ast_bridge_destroy(struct ast_bridge *bridge, int cause)
Destroy a bridge.
Definition: bridge.c:944
void ast_party_connected_line_free(struct ast_party_connected_line *doomed)
Destroy the connected line information contents.
Definition: channel.c:2072
const char * pbx_builtin_getvar_helper(struct ast_channel *chan, const char *name)
Return a pointer to the value of the corresponding channel variable.
#define agent_lock(agent)
Lock the agent.
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
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
int ast_check_hangup(struct ast_channel *chan)
Check to see if a channel is needing hang up.
Definition: channel.c:445
#define ast_bridge_channel_unlock(bridge_channel)
Unlock the bridge_channel.
Structure that contains information about a bridge.
Definition: bridge.h:349
Structure representing an agent.
Connected Line/Party information.
Definition: channel.h:456
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 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...
struct ast_bridge * ast_bridge_basic_new(void)
Create a new basic class bridge.
Structure that contains information regarding a channel in a bridge.
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:8293
#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:941
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application's arguments.
#define AST_APP_ARG(name)
Define an application argument.
static struct ao2_container * agents
static AO2_GLOBAL_OBJ_STATIC ( agent_holding  )
static

Agent holding bridge instance holder.

static void bridge_agent_hold_dissolving ( struct ast_bridge self)
static

The bridge is being dissolved.

Parameters
selfBridge to operate upon.

The bridge is being dissolved. Remove any external references to the bridge so it can be destroyed.

Note
On entry, self must NOT be locked.

Definition at line 1372 of file app_agent_pool.c.

References ao2_global_obj_release, ast_bridge_base_v_table, and ast_bridge_methods::dissolving.

1373 {
1374  ao2_global_obj_release(agent_holding);
1376 }
ast_bridge_dissolving_fn dissolving
Definition: bridge.h:263
struct ast_bridge_methods ast_bridge_base_v_table
Bridge base class virtual method table.
Definition: bridge.c:923
#define ao2_global_obj_release(holder)
Release the ao2 object held in the global holder.
Definition: astobj2.h:859
static int bridge_agent_hold_push ( struct ast_bridge self,
struct ast_bridge_channel bridge_channel,
struct ast_bridge_channel swap 
)
static
Todo:
XXX the login probation time should be only if it is needed.

Need to determine if there are any local channels that can optimize and wait until they actually do before leaving the AGENT_STATE_PROBATION_WAIT state. For now, the blind timer of LOGIN_WAIT_TIMEOUT_TIME will do.

Definition at line 1184 of file app_agent_pool.c.

References __ao2_cleanup(), agent_cfg::ack_call, agent_lock, AGENT_STATE_CALL_PRESENT, AGENT_STATE_CALL_WAIT_ACK, AGENT_STATE_CALL_WRAPUP, AGENT_STATE_LOGGED_OUT, AGENT_STATE_ON_CALL, AGENT_STATE_PROBATION_WAIT, AGENT_STATE_READY_FOR_CALL, agent_unlock, ao2_ref, ast_bridge_base_v_table, ast_bridge_channel_leave_bridge(), ast_bridge_dtmf_hook(), AST_BRIDGE_HOOK_REMOVE_ON_PULL, ast_bridge_interval_hook(), ast_bridge_set_after_callback(), ast_channel_add_bridge_role(), ast_channel_ref, ast_channel_remove_bridge_role(), ast_channel_set_bridge_role_option(), ast_channel_unref, ast_copy_string(), ast_debug, AST_DEVICE_NOT_INUSE, ast_strdupa, BRIDGE_CHANNEL_STATE_END, agent_pvt::cfg, ast_bridge_channel::chan, agent_pvt::devstate, agent_cfg::dtmf_accept, ast_bridge_channel::features, agent_pvt::logged, agent_cfg::moh, agent_pvt::override_ack_call, agent_pvt::override_dtmf_accept, agent_pvt::override_wrapup_time, agent_pvt::probation_start, ast_bridge_methods::push, RAII_VAR, agent_pvt::state, agent_pvt::username, and agent_cfg::wrapup_time.

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);
1212  if (ast_test_flag(agent, AGENT_FLAG_ACK_CALL)
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);
1223  if (ast_bridge_dtmf_hook(bridge_channel->features, dtmf, bridge_agent_hold_ack,
1225  ao2_ref(agent, -1);
1226  res = -1;
1227  }
1228  }
1229 
1230  /* Add heartbeat interval hook. */
1231  ao2_ref(agent, +1);
1232  if (ast_bridge_interval_hook(bridge_channel->features, 0, 1000,
1233  bridge_agent_hold_heartbeat, agent, __ao2_cleanup, AST_BRIDGE_HOOK_REMOVE_ON_PULL)) {
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) {
1245  res = ast_bridge_set_after_callback(chan, agent_after_bridge_cb,
1246  agent_after_bridge_cb_failed, chan);
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  */
1264  AST_CAUSE_NORMAL_CLEARING);
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);
1290  agent->state = AGENT_STATE_PROBATION_WAIT;
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 */
1311  agent->state = AGENT_STATE_READY_FOR_CALL;
1312  agent->devstate = AST_DEVICE_NOT_INUSE;
1313  agent_unlock(agent);
1314  ast_debug(1, "Agent %s: Call abort recovery complete.\n", agent->username);
1315  agent_devstate_changed(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 {
1326  agent->state = AGENT_STATE_READY_FOR_CALL;
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);
1333  agent_devstate_changed(agent->username);
1334  }
1335  break;
1336  }
1337 
1338  return 0;
1339 }
Main Channel structure associated with a channel.
struct ast_bridge_features * features
#define agent_unlock(agent)
Unlock the agent.
#define ast_channel_unref(c)
Decrease channel reference count.
Definition: channel.h:2958
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
void __ao2_cleanup(void *obj)
Definition: astobj2.c:677
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_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
struct ast_bridge_methods ast_bridge_base_v_table
Bridge base class virtual method table.
Definition: bridge.c:923
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
#define agent_lock(agent)
Lock the agent.
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
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
#define ast_debug(level,...)
Log a DEBUG message.
Structure representing an agent.
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_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
struct ast_channel * chan
#define ast_channel_ref(c)
Increase channel reference count.
Definition: channel.h:2947
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:425
ast_bridge_push_channel_fn push
Definition: bridge.h:265
#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:941
static struct ao2_container * agents

Variable Documentation

struct ast_custom_function agent_function
static
Initial value:
= {
.name = "AGENT",
.read = agent_function_read,
}

Definition at line 2233 of file app_agent_pool.c.

ast_mutex_t agent_holding_lock = { PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP , NULL, {1, 0} }
static

Agent holding bridge deferred creation lock.

Definition at line 995 of file app_agent_pool.c.

struct ao2_container* agents
static

Container of defined agents.

Definition at line 649 of file app_agent_pool.c.

struct aco_file agents_conf
static
Initial value:
= {
.filename = "agents.conf",
.types = ACO_TYPES(&general_type, &agent_type),
}
#define ACO_TYPES(...)
A helper macro to ensure that aco_info types always have a sentinel.

Definition at line 485 of file app_agent_pool.c.