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"

Include dependency graph for app_agent_pool.c:

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

Defines

#define agent_lock(agent)   _agent_lock(agent, __FILE__, __PRETTY_FUNCTION__, __LINE__, #agent)
 Lock the agent.
#define agent_unlock(agent)   _agent_unlock(agent, __FILE__, __PRETTY_FUNCTION__, __LINE__, #agent)
 Unlock the agent.
#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.
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.
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 (agent_holding)
static AO2_GLOBAL_OBJ_STATIC (cfg_handle)
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.
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 = AST_BUILDOPT_SUM, .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_MUTEX_INITIALIZER , NULL, 1 }
static 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 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 <rmudgett@digium.com>
See Also:

Definition in file app_agent_pool.c.


Define Documentation

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

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

#define AST_MAX_BUF   256

Definition at line 338 of file app_agent_pool.c.

Referenced by action_agents().

#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 341 of file app_agent_pool.c.

Referenced by agent_request_exec().

#define FORMAT_HDR   "%-8s %-20s %-11s %-30s %s\n"

#define FORMAT_ROW   "%-8s %-20s %-11s %-30s %s\n"

#define LOGIN_WAIT_TIMEOUT_TIME   5

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

Definition at line 344 of file app_agent_pool.c.

Referenced by bridge_agent_hold_heartbeat().


Enumeration Type Documentation

Enumerator:
OPT_SILENT 

Definition at line 2091 of file app_agent_pool.c.

02091                            {
02092    OPT_SILENT = (1 << 0),
02093 };

Agent config option override flags.

Enumerator:
AGENT_FLAG_ACK_CALL 
AGENT_FLAG_DTMF_ACCEPT 
AGENT_FLAG_AUTO_LOGOFF 
AGENT_FLAG_WRAPUP_TIME 

Definition at line 581 of file app_agent_pool.c.

00581                           {
00582    AGENT_FLAG_ACK_CALL = (1 << 0),
00583    AGENT_FLAG_DTMF_ACCEPT = (1 << 1),
00584    AGENT_FLAG_AUTO_LOGOFF = (1 << 2),
00585    AGENT_FLAG_WRAPUP_TIME = (1 << 3),
00586 };

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 561 of file app_agent_pool.c.

00561                  {
00562    /*! The agent is defined but an agent is not present. */
00563    AGENT_STATE_LOGGED_OUT,
00564    /*! Forced initial login wait to allow any local channel optimizations to happen. */
00565    AGENT_STATE_PROBATION_WAIT,
00566    /*! The agent is ready for a call. */
00567    AGENT_STATE_READY_FOR_CALL,
00568    /*! The agent has a call waiting to connect. */
00569    AGENT_STATE_CALL_PRESENT,
00570    /*! The agent needs to ack the call. */
00571    AGENT_STATE_CALL_WAIT_ACK,
00572    /*! The agent is connected with a call. */
00573    AGENT_STATE_ON_CALL,
00574    /*! The agent is resting between calls. */
00575    AGENT_STATE_CALL_WRAPUP,
00576    /*! The agent is being kicked out. */
00577    AGENT_STATE_LOGGING_OUT,
00578 };


Function Documentation

static void __reg_module ( void   )  [static]

Definition at line 2710 of file app_agent_pool.c.

static void __unreg_module ( void   )  [static]

Definition at line 2710 of file app_agent_pool.c.

static void _agent_lock ( struct agent_pvt agent,
const char *  file,
const char *  function,
int  line,
const char *  var 
) [inline, static]

Definition at line 654 of file app_agent_pool.c.

References __ao2_lock(), and AO2_LOCK_REQ_MUTEX.

00655 {
00656    __ao2_lock(agent, AO2_LOCK_REQ_MUTEX, file, function, line, var);
00657 }

static void _agent_unlock ( struct agent_pvt agent,
const char *  file,
const char *  function,
int  line,
const char *  var 
) [inline, static]

Definition at line 667 of file app_agent_pool.c.

References __ao2_unlock().

00668 {
00669    __ao2_unlock(agent, file, function, line, var);
00670 }

static int action_agent_logoff ( struct mansession s,
const struct message m 
) [static]

Definition at line 2600 of file app_agent_pool.c.

References agent_logoff_request(), ast_strlen_zero, ast_true(), astman_get_header(), astman_send_ack(), and astman_send_error().

Referenced by load_module().

02601 {
02602    const char *agent = astman_get_header(m, "Agent");
02603    const char *soft_s = astman_get_header(m, "Soft"); /* "true" is don't hangup */
02604 
02605    if (ast_strlen_zero(agent)) {
02606       astman_send_error(s, m, "No agent specified");
02607       return 0;
02608    }
02609 
02610    if (!agent_logoff_request(agent, ast_true(soft_s))) {
02611       astman_send_ack(s, m, "Agent logged out");
02612    } else {
02613       astman_send_error(s, m, "No such agent");
02614    }
02615 
02616    return 0;
02617 }

static int action_agents ( struct mansession s,
const struct message m 
) [static]

Definition at line 2522 of file app_agent_pool.c.

References agent_lock, agent_lock_logged(), agent_unlock, ao2_cleanup, ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_ref, ast_channel_snapshot_create(), ast_channel_unlock, ast_channel_unref, ast_free, ast_manager_build_channel_state_string(), AST_MAX_BUF, ast_str_alloca, ast_str_append(), ast_str_buffer(), ast_str_set(), ast_strlen_zero, astman_append(), astman_get_header(), astman_send_list_complete_end(), astman_send_list_complete_start(), astman_send_listack(), agent_pvt::call_start, agent_pvt::cfg, agent_cfg::full_name, agent_pvt::login_start, out, pbx_builtin_getvar_helper(), RAII_VAR, and agent_pvt::username.

Referenced by load_module().

02523 {
02524    const char *id = astman_get_header(m, "ActionID");
02525    char id_text[AST_MAX_BUF];
02526    struct ao2_iterator iter;
02527    struct agent_pvt *agent;
02528    struct ast_str *out = ast_str_alloca(4096);
02529    int num_agents = 0;
02530 
02531    if (!ast_strlen_zero(id)) {
02532       snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", id);
02533    } else {
02534       id_text[0] = '\0';
02535    }
02536    astman_send_listack(s, m, "Agents will follow", "start");
02537 
02538    iter = ao2_iterator_init(agents, 0);
02539    for (; (agent = ao2_iterator_next(&iter)); ao2_ref(agent, -1)) {
02540       struct ast_channel *logged;
02541 
02542       agent_lock(agent);
02543       logged = agent_lock_logged(agent);
02544 
02545       /*
02546        * Status Values:
02547        * AGENT_LOGGEDOFF - Agent isn't logged in
02548        * AGENT_IDLE      - Agent is logged in, and waiting for call
02549        * AGENT_ONCALL    - Agent is logged in, and on a call
02550        * AGENT_UNKNOWN   - Don't know anything about agent. Shouldn't ever get this.
02551        */
02552       ast_str_set(&out, 0, "Agent: %s\r\n", agent->username);
02553       ast_str_append(&out, 0, "Name: %s\r\n", agent->cfg->full_name);
02554 
02555       if (logged) {
02556          const char *talking_to_chan;
02557          struct ast_str *logged_headers;
02558          RAII_VAR(struct ast_channel_snapshot *, logged_snapshot, ast_channel_snapshot_create(logged), ao2_cleanup);
02559 
02560          if (!logged_snapshot
02561             || !(logged_headers =
02562                 ast_manager_build_channel_state_string(logged_snapshot))) {
02563             ast_channel_unlock(logged);
02564             ast_channel_unref(logged);
02565             agent_unlock(agent);
02566             continue;
02567          }
02568 
02569          talking_to_chan = pbx_builtin_getvar_helper(logged, "BRIDGEPEER");
02570          if (!ast_strlen_zero(talking_to_chan)) {
02571             ast_str_append(&out, 0, "Status: %s\r\n", "AGENT_ONCALL");
02572             ast_str_append(&out, 0, "TalkingToChan: %s\r\n", talking_to_chan);
02573             ast_str_append(&out, 0, "CallStarted: %ld\n", (long) agent->call_start);
02574          } else {
02575             ast_str_append(&out, 0, "Status: %s\r\n", "AGENT_IDLE");
02576          }
02577          ast_str_append(&out, 0, "LoggedInTime: %ld\r\n", (long) agent->login_start);
02578          ast_str_append(&out, 0, "%s", ast_str_buffer(logged_headers));
02579          ast_channel_unlock(logged);
02580          ast_channel_unref(logged);
02581          ast_free(logged_headers);
02582       } else {
02583          ast_str_append(&out, 0, "Status: %s\r\n", "AGENT_LOGGEDOFF");
02584       }
02585 
02586       agent_unlock(agent);
02587 
02588       astman_append(s, "Event: Agents\r\n"
02589          "%s%s\r\n",
02590          ast_str_buffer(out), id_text);
02591       ++num_agents;
02592    }
02593    ao2_iterator_destroy(&iter);
02594 
02595    astman_send_list_complete_start(s, m, "AgentsComplete", num_agents);
02596    astman_send_list_complete_end(s);
02597    return 0;
02598 }

static void agent_after_bridge_cb ( struct ast_channel chan,
void *  data 
) [static]

Definition at line 1613 of file app_agent_pool.c.

References agent_run(), ao2_find, ao2_ref, ast_channel_name(), ast_debug, and agent_pvt::username.

Referenced by bridge_agent_hold_push().

01614 {
01615    struct agent_pvt *agent;
01616 
01617    agent = ao2_find(agents, chan, 0);
01618    if (!agent) {
01619       return;
01620    }
01621 
01622    ast_debug(1, "Agent %s: New agent channel %s.\n",
01623       agent->username, ast_channel_name(chan));
01624    agent_run(agent, chan);
01625    ao2_ref(agent, -1);
01626 }

static void agent_after_bridge_cb_failed ( enum ast_bridge_after_cb_reason  reason,
void *  data 
) [static]

Definition at line 1628 of file app_agent_pool.c.

References agent_lock, agent_logout(), ao2_find, ao2_ref, ast_bridge_after_cb_reason_string(), ast_channel_name(), ast_log, LOG_WARNING, and agent_pvt::username.

Referenced by bridge_agent_hold_push().

01629 {
01630    struct ast_channel *chan = data;
01631    struct agent_pvt *agent;
01632 
01633    agent = ao2_find(agents, chan, 0);
01634    if (!agent) {
01635       return;
01636    }
01637    ast_log(LOG_WARNING, "Agent %s: Forced logout.  Lost control of %s because: %s\n",
01638       agent->username, ast_channel_name(chan),
01639       ast_bridge_after_cb_reason_string(reason));
01640    agent_lock(agent);
01641    agent_logout(agent);
01642    ao2_ref(agent, -1);
01643 }

static void agent_alert ( struct ast_bridge_channel bridge_channel,
const void *  payload,
size_t  payload_size 
) [static]

Definition at line 1733 of file app_agent_pool.c.

References agent_cfg::ack_call, agent_pvt::ack_time, agent_connect_caller(), AGENT_FLAG_ACK_CALL, AGENT_FLAG_DTMF_ACCEPT, agent_lock, AGENT_STATE_CALL_PRESENT, AGENT_STATE_CALL_WAIT_ACK, agent_unlock, ao2_find, ao2_ref, ast_bridge_channel_clear_roles(), ast_bridge_channel_establish_roles(), ast_bridge_channel_feature_digit(), ast_bridge_channel_lock_bridge(), ast_bridge_unlock, ast_channel_set_bridge_role_option(), ast_debug, AST_DIGIT_ANY, ast_stopstream(), ast_strdupa, ast_stream_and_wait(), ast_strlen_zero, ast_test_flag, ast_tvnow(), agent_cfg::beep_sound, ast_bridge_channel::bridge, agent_pvt::cfg, ast_bridge_channel::chan, digit, agent_cfg::dtmf_accept, NULL, OBJ_KEY, agent_pvt::override_ack_call, agent_pvt::override_dtmf_accept, agent_pvt::state, and agent_pvt::username.

Referenced by send_alert_to_agent().

01734 {
01735    const char *agent_id = payload;
01736    const char *playfile;
01737    const char *dtmf_accept;
01738    struct agent_pvt *agent;
01739    int digit;
01740    char dtmf[2];
01741 
01742    agent = ao2_find(agents, agent_id, OBJ_KEY);
01743    if (!agent) {
01744       ast_debug(1, "Agent '%s' does not exist.  Where did it go?\n", agent_id);
01745       return;
01746    }
01747 
01748    /* Change holding bridge participant role's idle mode to silence */
01749    ast_bridge_channel_lock_bridge(bridge_channel);
01750    ast_bridge_channel_clear_roles(bridge_channel);
01751    ast_channel_set_bridge_role_option(bridge_channel->chan, "holding_participant", "idle_mode", "silence");
01752    ast_bridge_channel_establish_roles(bridge_channel);
01753    ast_bridge_unlock(bridge_channel->bridge);
01754 
01755    agent_lock(agent);
01756    playfile = ast_strdupa(agent->cfg->beep_sound);
01757 
01758    /* Determine which DTMF digits interrupt the alerting signal. */
01759    if (ast_test_flag(agent, AGENT_FLAG_ACK_CALL)
01760       ? agent->override_ack_call : agent->cfg->ack_call) {
01761       dtmf_accept = ast_test_flag(agent, AGENT_FLAG_DTMF_ACCEPT)
01762          ? agent->override_dtmf_accept : agent->cfg->dtmf_accept;
01763 
01764       /* Only the first digit of the ack will stop playback. */
01765       dtmf[0] = *dtmf_accept;
01766       dtmf[1] = '\0';
01767       dtmf_accept = dtmf;
01768    } else {
01769       dtmf_accept = NULL;
01770    }
01771    agent_unlock(agent);
01772 
01773    /* Alert the agent. */
01774    digit = ast_stream_and_wait(bridge_channel->chan, playfile,
01775       ast_strlen_zero(dtmf_accept) ? AST_DIGIT_ANY : dtmf_accept);
01776    ast_stopstream(bridge_channel->chan);
01777 
01778    agent_lock(agent);
01779    switch (agent->state) {
01780    case AGENT_STATE_CALL_PRESENT:
01781       if (!ast_strlen_zero(dtmf_accept)) {
01782          agent->state = AGENT_STATE_CALL_WAIT_ACK;
01783          agent->ack_time = ast_tvnow();
01784 
01785          if (0 < digit) {
01786             /* Playback was interrupted by a digit. */
01787             agent_unlock(agent);
01788             ao2_ref(agent, -1);
01789             ast_bridge_channel_feature_digit(bridge_channel, digit);
01790             return;
01791          }
01792          break;
01793       }
01794 
01795       /* Connect to caller now. */
01796       ast_debug(1, "Agent %s: Immediately connecting to call.\n", agent->username);
01797       agent_connect_caller(bridge_channel, agent);/* Will unlock agent. */
01798       ao2_ref(agent, -1);
01799       return;
01800    default:
01801       break;
01802    }
01803    agent_unlock(agent);
01804    ao2_ref(agent, -1);
01805 }

static struct ast_bridge_channel* agent_bridge_channel_get_lock ( struct agent_pvt agent  )  [static, read]

Definition at line 1655 of file app_agent_pool.c.

References agent_lock, agent_unlock, ao2_ref, ast_bridge_channel_lock, ast_bridge_channel_unlock, ast_channel_get_bridge_channel(), ast_channel_lock, ast_channel_ref, ast_channel_unlock, ast_channel_unref, bc, ast_bridge_channel::chan, agent_pvt::logged, and NULL.

Referenced by agent_request_exec(), caller_abort_agent(), and caller_joined_bridge().

01656 {
01657    struct ast_channel *logged;
01658    struct ast_bridge_channel *bc;
01659 
01660    for (;;) {
01661       agent_lock(agent);
01662       logged = agent->logged;
01663       if (!logged) {
01664          agent_unlock(agent);
01665          return NULL;
01666       }
01667       ast_channel_ref(logged);
01668       agent_unlock(agent);
01669 
01670       ast_channel_lock(logged);
01671       bc = ast_channel_get_bridge_channel(logged);
01672       ast_channel_unlock(logged);
01673       ast_channel_unref(logged);
01674       if (!bc) {
01675          if (agent->logged != logged) {
01676             continue;
01677          }
01678          return NULL;
01679       }
01680 
01681       ast_bridge_channel_lock(bc);
01682       if (bc->chan != logged || agent->logged != logged) {
01683          ast_bridge_channel_unlock(bc);
01684          ao2_ref(bc, -1);
01685          continue;
01686       }
01687       return bc;
01688    }
01689 }

static void* agent_cfg_alloc ( const char *  name  )  [static]

Definition at line 436 of file app_agent_pool.c.

References agent_cfg_destructor(), AO2_ALLOC_OPT_LOCK_NOLOCK, ao2_alloc_options, ast_string_field_init, ast_string_field_set, NULL, and agent_cfg::username.

00437 {
00438    struct agent_cfg *cfg;
00439 
00440    cfg = ao2_alloc_options(sizeof(*cfg), agent_cfg_destructor,
00441       AO2_ALLOC_OPT_LOCK_NOLOCK);
00442    if (!cfg || ast_string_field_init(cfg, 64)) {
00443       return NULL;
00444    }
00445    ast_string_field_set(cfg, username, name);
00446    return cfg;
00447 }

static void agent_cfg_destructor ( void *  vdoomed  )  [static]

Definition at line 429 of file app_agent_pool.c.

References ast_string_field_free_memory.

Referenced by agent_cfg_alloc().

00430 {
00431    struct agent_cfg *doomed = vdoomed;
00432 
00433    ast_string_field_free_memory(doomed);
00434 }

static void* agent_cfg_find ( struct ao2_container agents,
const char *  username 
) [static]

Definition at line 449 of file app_agent_pool.c.

References ao2_find, and OBJ_KEY.

00450 {
00451    return ao2_find(agents, username, OBJ_KEY);
00452 }

static int agent_cfg_sort_cmp ( const void *  obj_left,
const void *  obj_right,
int  flags 
) [static]

Definition at line 407 of file app_agent_pool.c.

References OBJ_KEY, OBJ_PARTIAL_KEY, OBJ_POINTER, and agent_cfg::username.

Referenced by agents_cfg_alloc().

00408 {
00409    const struct agent_cfg *cfg_left = obj_left;
00410    const struct agent_cfg *cfg_right = obj_right;
00411    const char *right_key = obj_right;
00412    int cmp;
00413 
00414    switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
00415    default:
00416    case OBJ_POINTER:
00417       right_key = cfg_right->username;
00418       /* Fall through */
00419    case OBJ_KEY:
00420       cmp = strcmp(cfg_left->username, right_key);
00421       break;
00422    case OBJ_PARTIAL_KEY:
00423       cmp = strncmp(cfg_left->username, right_key, strlen(right_key));
00424       break;
00425    }
00426    return cmp;
00427 }

static void agent_connect_caller ( struct ast_bridge_channel bridge_channel,
struct agent_pvt agent 
) [static]

Definition at line 1029 of file app_agent_pool.c.

References AGENT_STATE_ON_CALL, agent_unlock, ao2_t_ref, AST_BRIDGE_BUILTIN_AUTOMIXMON, ast_bridge_channel_leave_bridge(), ast_bridge_channel_write_callback(), ast_bridge_channel_write_control_data(), ast_bridge_destroy(), ast_bridge_features_do(), ast_bridge_move(), AST_CAUSE_NORMAL_CLEARING, AST_CONTROL_ANSWER, AUTO_MONITOR_START, ast_bridge_channel::bridge, BRIDGE_CHANNEL_STATE_END, agent_pvt::call_start, agent_pvt::caller_bridge, agent_pvt::cfg, ast_bridge_channel::chan, clear_agent_status(), NULL, agent_cfg::record_agent_calls, ast_bridge_features_automixmonitor::start_stop, and agent_pvt::state.

Referenced by agent_alert(), and bridge_agent_hold_ack().

01030 {
01031    struct ast_bridge *caller_bridge;
01032    int record_agent_calls;
01033    int res;
01034 
01035    record_agent_calls = agent->cfg->record_agent_calls;
01036    caller_bridge = agent->caller_bridge;
01037    agent->caller_bridge = NULL;
01038    agent->state = AGENT_STATE_ON_CALL;
01039    time(&agent->call_start);
01040    agent_unlock(agent);
01041 
01042    if (!caller_bridge) {
01043       /* Reset agent. */
01044       ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END,
01045          AST_CAUSE_NORMAL_CLEARING);
01046       return;
01047    }
01048    res = ast_bridge_move(caller_bridge, bridge_channel->bridge, bridge_channel->chan,
01049       NULL, 0);
01050    if (res) {
01051       /* Reset agent. */
01052       ast_bridge_destroy(caller_bridge, 0);
01053       ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END,
01054          AST_CAUSE_NORMAL_CLEARING);
01055       return;
01056    }
01057    res = ast_bridge_channel_write_control_data(bridge_channel, AST_CONTROL_ANSWER, NULL, 0)
01058       || ast_bridge_channel_write_callback(bridge_channel, 0, clear_agent_status, NULL, 0);
01059    if (res) {
01060       /* Reset agent. */
01061       ast_bridge_destroy(caller_bridge, 0);
01062       return;
01063    }
01064 
01065    if (record_agent_calls) {
01066       struct ast_bridge_features_automixmonitor options = {
01067          .start_stop = AUTO_MONITOR_START,
01068          };
01069 
01070       /*
01071        * The agent is in the new bridge so we can invoke the
01072        * mixmonitor hook to only start recording.
01073        */
01074       ast_bridge_features_do(AST_BRIDGE_BUILTIN_AUTOMIXMON, bridge_channel, &options);
01075    }
01076 
01077    ao2_t_ref(caller_bridge, -1, "Agent successfully in caller_bridge");
01078 }

static void agent_devstate_changed ( const char *  agent_id  )  [static]

static int agent_function_read ( struct ast_channel chan,
const char *  cmd,
char *  data,
char *  buf,
size_t  len 
) [static]

Definition at line 2179 of file app_agent_pool.c.

References agent_lock, agent_lock_logged(), agent_unlock, ao2_find, ao2_ref, args, AST_APP_ARG, ast_channel_name(), ast_channel_unlock, ast_channel_unref, ast_copy_string(), AST_DECLARE_APP_ARGS, ast_log, AST_NONSTANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero, agent_pvt::cfg, agent_cfg::full_name, item, LOG_WARNING, agent_pvt::logged, agent_cfg::moh, OBJ_KEY, parse(), and status.

02180 {
02181    char *parse;
02182    struct agent_pvt *agent;
02183    struct ast_channel *logged;
02184    AST_DECLARE_APP_ARGS(args,
02185       AST_APP_ARG(agentid);
02186       AST_APP_ARG(item);
02187    );
02188 
02189    buf[0] = '\0';
02190 
02191    parse = ast_strdupa(data ?: "");
02192    AST_NONSTANDARD_APP_ARGS(args, parse, ':');
02193 
02194    if (ast_strlen_zero(args.agentid)) {
02195       ast_log(LOG_WARNING, "The AGENT function requires an argument - agentid!\n");
02196       return -1;
02197    }
02198    if (!args.item) {
02199       args.item = "status";
02200    }
02201 
02202    agent = ao2_find(agents, args.agentid, OBJ_KEY);
02203    if (!agent) {
02204       ast_log(LOG_WARNING, "Agent '%s' not found!\n", args.agentid);
02205       return -1;
02206    }
02207 
02208    agent_lock(agent);
02209    if (!strcasecmp(args.item, "status")) {
02210       const char *status;
02211 
02212       if (agent->logged) {
02213          status = "LOGGEDIN";
02214       } else {
02215          status = "LOGGEDOUT";
02216       }
02217       ast_copy_string(buf, status, len);
02218    } else if (!strcasecmp(args.item, "name")) {
02219       ast_copy_string(buf, agent->cfg->full_name, len);
02220    } else if (!strcasecmp(args.item, "mohclass")) {
02221       ast_copy_string(buf, agent->cfg->moh, len);
02222    } else if (!strcasecmp(args.item, "channel")) {
02223       logged = agent_lock_logged(agent);
02224       if (logged) {
02225          char *pos;
02226 
02227          ast_copy_string(buf, ast_channel_name(logged), len);
02228          ast_channel_unlock(logged);
02229          ast_channel_unref(logged);
02230 
02231          pos = strrchr(buf, '-');
02232          if (pos) {
02233             *pos = '\0';
02234          }
02235       }
02236    } else if (!strcasecmp(args.item, "fullchannel")) {
02237       logged = agent_lock_logged(agent);
02238       if (logged) {
02239          ast_copy_string(buf, ast_channel_name(logged), len);
02240          ast_channel_unlock(logged);
02241          ast_channel_unref(logged);
02242       }
02243    }
02244    agent_unlock(agent);
02245    ao2_ref(agent, -1);
02246 
02247    return 0;
02248 }

static char* agent_handle_logoff_cmd ( struct ast_cli_entry e,
int  cmd,
struct ast_cli_args a 
) [static]

Definition at line 2480 of file app_agent_pool.c.

References agent_logoff_request(), ast_cli_args::argc, ast_cli_args::argv, ast_cli(), ast_strdup, ast_strlen_zero, CLI_GENERATE, CLI_INIT, CLI_SHOWUSAGE, CLI_SUCCESS, ast_cli_entry::command, complete_agent_logoff(), ast_cli_args::fd, ast_cli_args::n, NULL, ast_cli_args::pos, ast_cli_entry::usage, and ast_cli_args::word.

02481 {
02482    switch (cmd) {
02483    case CLI_INIT:
02484       e->command = "agent logoff";
02485       e->usage =
02486          "Usage: agent logoff <agent-id> [soft]\n"
02487          "       Sets an agent as no longer logged in.\n"
02488          "       If 'soft' is specified, do not hangup existing calls.\n";
02489       return NULL;
02490    case CLI_GENERATE:
02491       if (a->pos == 2) {
02492          return complete_agent_logoff(a->word, a->n);
02493       } else if (a->pos == 3 && a->n == 0
02494          && (ast_strlen_zero(a->word)
02495             || !strncasecmp("soft", a->word, strlen(a->word)))) {
02496          return ast_strdup("soft");
02497       }
02498       return NULL;
02499    }
02500 
02501    if (a->argc < 3 || 4 < a->argc) {
02502       return CLI_SHOWUSAGE;
02503    }
02504    if (a->argc == 4 && strcasecmp(a->argv[3], "soft")) {
02505       return CLI_SHOWUSAGE;
02506    }
02507 
02508    if (!agent_logoff_request(a->argv[2], a->argc == 4)) {
02509       ast_cli(a->fd, "Logging out %s\n", a->argv[2]);
02510    }
02511 
02512    return CLI_SUCCESS;
02513 }

static char* agent_handle_show_all ( struct ast_cli_entry e,
int  cmd,
struct ast_cli_args a 
) [static]

Definition at line 2399 of file app_agent_pool.c.

References agent_show_requested(), ast_cli_args::argc, CLI_GENERATE, CLI_INIT, CLI_SHOWUSAGE, CLI_SUCCESS, ast_cli_entry::command, NULL, and ast_cli_entry::usage.

02400 {
02401    switch (cmd) {
02402    case CLI_INIT:
02403       e->command = "agent show all";
02404       e->usage =
02405          "Usage: agent show all\n"
02406          "       Provides summary information for all agents.\n";
02407       return NULL;
02408    case CLI_GENERATE:
02409       return NULL;
02410    }
02411 
02412    if (a->argc != 3) {
02413       return CLI_SHOWUSAGE;
02414    }
02415 
02416    agent_show_requested(a, 0);
02417 
02418    return CLI_SUCCESS;
02419 }

static char* agent_handle_show_online ( struct ast_cli_entry e,
int  cmd,
struct ast_cli_args a 
) [static]

Definition at line 2377 of file app_agent_pool.c.

References agent_show_requested(), ast_cli_args::argc, CLI_GENERATE, CLI_INIT, CLI_SHOWUSAGE, CLI_SUCCESS, ast_cli_entry::command, NULL, and ast_cli_entry::usage.

02378 {
02379    switch (cmd) {
02380    case CLI_INIT:
02381       e->command = "agent show online";
02382       e->usage =
02383          "Usage: agent show online\n"
02384          "       Provides summary information for logged in agents.\n";
02385       return NULL;
02386    case CLI_GENERATE:
02387       return NULL;
02388    }
02389 
02390    if (a->argc != 3) {
02391       return CLI_SHOWUSAGE;
02392    }
02393 
02394    agent_show_requested(a, 1);
02395 
02396    return CLI_SUCCESS;
02397 }

static char* agent_handle_show_specific ( struct ast_cli_entry e,
int  cmd,
struct ast_cli_args a 
) [static]

Definition at line 2421 of file app_agent_pool.c.

References agent_lock, agent_lock_logged(), agent_unlock, ao2_find, ao2_ref, ast_cli_args::argc, ast_cli_args::argv, ast_channel_name(), ast_channel_unlock, ast_channel_unref, ast_cli(), AST_CLI_YESNO, ast_devstate_str(), ast_str_alloca, ast_str_append(), ast_str_buffer(), ast_str_set(), ast_strlen_zero, agent_cfg::beep_sound, agent_pvt::call_start, agent_pvt::cfg, CLI_GENERATE, CLI_INIT, CLI_SHOWUSAGE, CLI_SUCCESS, ast_cli_entry::command, complete_agent(), agent_pvt::devstate, ast_cli_args::fd, agent_cfg::full_name, agent_pvt::login_start, agent_cfg::moh, ast_cli_args::n, NULL, OBJ_KEY, out, pbx_builtin_getvar_helper(), ast_cli_args::pos, agent_cfg::record_agent_calls, ast_cli_entry::usage, agent_pvt::username, and ast_cli_args::word.

02422 {
02423    struct agent_pvt *agent;
02424    struct ast_channel *logged;
02425    struct ast_str *out = ast_str_alloca(4096);
02426 
02427    switch (cmd) {
02428    case CLI_INIT:
02429       e->command = "agent show";
02430       e->usage =
02431          "Usage: agent show <agent-id>\n"
02432          "       Show information about the <agent-id> agent\n";
02433       return NULL;
02434    case CLI_GENERATE:
02435       if (a->pos == 2) {
02436          return complete_agent(a->word, a->n);
02437       }
02438       return NULL;
02439    }
02440 
02441    if (a->argc != 3) {
02442       return CLI_SHOWUSAGE;
02443    }
02444 
02445    agent = ao2_find(agents, a->argv[2], OBJ_KEY);
02446    if (!agent) {
02447       ast_cli(a->fd, "Agent '%s' not found\n", a->argv[2]);
02448       return CLI_SUCCESS;
02449    }
02450 
02451    agent_lock(agent);
02452    logged = agent_lock_logged(agent);
02453    ast_str_set(&out, 0, "Id: %s\n", agent->username);
02454    ast_str_append(&out, 0, "Name: %s\n", agent->cfg->full_name);
02455    ast_str_append(&out, 0, "Beep: %s\n", agent->cfg->beep_sound);
02456    ast_str_append(&out, 0, "MOH: %s\n", agent->cfg->moh);
02457    ast_str_append(&out, 0, "RecordCalls: %s\n", AST_CLI_YESNO(agent->cfg->record_agent_calls));
02458    ast_str_append(&out, 0, "State: %s\n", ast_devstate_str(agent->devstate));
02459    if (logged) {
02460       const char *talking_with;
02461 
02462       ast_str_append(&out, 0, "LoggedInChannel: %s\n", ast_channel_name(logged));
02463       ast_str_append(&out, 0, "LoggedInTime: %ld\n", (long) agent->login_start);
02464       talking_with = pbx_builtin_getvar_helper(logged, "BRIDGEPEER");
02465       if (!ast_strlen_zero(talking_with)) {
02466          ast_str_append(&out, 0, "TalkingWith: %s\n", talking_with);
02467          ast_str_append(&out, 0, "CallStarted: %ld\n", (long) agent->call_start);
02468       }
02469       ast_channel_unlock(logged);
02470       ast_channel_unref(logged);
02471    }
02472    agent_unlock(agent);
02473    ao2_ref(agent, -1);
02474 
02475    ast_cli(a->fd, "%s", ast_str_buffer(out));
02476 
02477    return CLI_SUCCESS;
02478 }

static struct ast_channel* agent_lock_logged ( struct agent_pvt agent  )  [static, read]

Definition at line 685 of file app_agent_pool.c.

References agent_lock, agent_unlock, ast_channel_lock, ast_channel_ref, ast_channel_unlock, ast_channel_unref, agent_pvt::logged, and NULL.

Referenced by action_agents(), agent_function_read(), agent_handle_show_specific(), agent_logoff_request(), and agent_show_requested().

00686 {
00687    struct ast_channel *logged;
00688 
00689    for (;;) {
00690       if (!agent->logged) { /* No owner. Nothing to do. */
00691          return NULL;
00692       }
00693 
00694       /* If we don't ref the logged, it could be killed when we unlock the agent. */
00695       logged = ast_channel_ref(agent->logged);
00696 
00697       /* Locking logged requires us to lock channel, then agent. */
00698       agent_unlock(agent);
00699       ast_channel_lock(logged);
00700       agent_lock(agent);
00701 
00702       /* Check if logged changed during agent unlock period */
00703       if (logged != agent->logged) {
00704          /* Channel changed. Unref and do another pass. */
00705          ast_channel_unlock(logged);
00706          ast_channel_unref(logged);
00707       } else {
00708          /* Channel stayed the same. Return it. */
00709          return logged;
00710       }
00711    }
00712 }

static void agent_login_channel_config ( struct agent_pvt agent,
struct ast_channel chan 
) [static]

Definition at line 2035 of file app_agent_pool.c.

References AGENT_FLAG_ACK_CALL, AGENT_FLAG_AUTO_LOGOFF, AGENT_FLAG_DTMF_ACCEPT, AGENT_FLAG_WRAPUP_TIME, agent_lock, agent_unlock, ast_channel_connected(), ast_channel_lock, ast_channel_unlock, ast_copy_flags, AST_FLAGS_ALL, ast_party_connected_line_copy(), ast_party_connected_line_free(), ast_party_connected_line_init(), ast_set_flag, ast_strdupa, ast_string_field_set, ast_strlen_zero, ast_true(), NULL, agent_pvt::override_ack_call, agent_pvt::override_auto_logoff, agent_pvt::override_wrapup_time, pbx_builtin_getvar_helper(), var, and agent_pvt::waiting_colp.

Referenced by agent_login_exec().

02036 {
02037    struct ast_flags opts = { 0 };
02038    struct ast_party_connected_line connected;
02039    unsigned int override_ack_call = 0;
02040    unsigned int override_auto_logoff = 0;
02041    unsigned int override_wrapup_time = 0;
02042    const char *override_dtmf_accept = NULL;
02043    const char *var;
02044 
02045    ast_party_connected_line_init(&connected);
02046 
02047    /* Get config values from channel. */
02048    ast_channel_lock(chan);
02049    ast_party_connected_line_copy(&connected, ast_channel_connected(chan));
02050 
02051    var = pbx_builtin_getvar_helper(chan, "AGENTACKCALL");
02052    if (!ast_strlen_zero(var)) {
02053       override_ack_call = ast_true(var) ? 1 : 0;
02054       ast_set_flag(&opts, AGENT_FLAG_ACK_CALL);
02055    }
02056 
02057    var = pbx_builtin_getvar_helper(chan, "AGENTACCEPTDTMF");
02058    if (!ast_strlen_zero(var)) {
02059       override_dtmf_accept = ast_strdupa(var);
02060       ast_set_flag(&opts, AGENT_FLAG_DTMF_ACCEPT);
02061    }
02062 
02063    var = pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF");
02064    if (!ast_strlen_zero(var)) {
02065       if (sscanf(var, "%u", &override_auto_logoff) == 1) {
02066          ast_set_flag(&opts, AGENT_FLAG_AUTO_LOGOFF);
02067       }
02068    }
02069 
02070    var = pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME");
02071    if (!ast_strlen_zero(var)) {
02072       if (sscanf(var, "%u", &override_wrapup_time) == 1) {
02073          ast_set_flag(&opts, AGENT_FLAG_WRAPUP_TIME);
02074       }
02075    }
02076    ast_channel_unlock(chan);
02077 
02078    /* Set config values on agent. */
02079    agent_lock(agent);
02080    ast_party_connected_line_free(&agent->waiting_colp);
02081    agent->waiting_colp = connected;
02082 
02083    ast_string_field_set(agent, override_dtmf_accept, override_dtmf_accept);
02084    ast_copy_flags(agent, &opts, AST_FLAGS_ALL);
02085    agent->override_auto_logoff = override_auto_logoff;
02086    agent->override_wrapup_time = override_wrapup_time;
02087    agent->override_ack_call = override_ack_call;
02088    agent_unlock(agent);
02089 }

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

Dialplan AgentLogin application to log in an agent.

Parameters:
chan Channel attempting to login as an agent.
data Application parameters
Return values:
0 To continue in dialplan.
-1 To hangup.

Definition at line 2107 of file app_agent_pool.c.

References agent_lock, agent_login_channel_config(), agent_login_opts, agent_run(), agent_unlock, ao2_cleanup, ao2_find, args, ast_answer(), AST_APP_ARG, ast_app_parse_options(), ast_channel_lock, ast_channel_readformat(), ast_channel_ref, ast_channel_unlock, ast_channel_writeformat(), AST_DECLARE_APP_ARGS, AST_DIGIT_NONE, ast_format_get_name(), ast_log, AST_STANDARD_APP_ARGS, AST_STATE_UP, ast_strdupa, ast_stream_and_wait(), ast_strlen_zero, ast_test_flag, ast_tvnow(), ast_verb, bridge_agent_hold_deferred_create(), LOG_WARNING, NULL, OBJ_KEY, OPT_SILENT, parse(), pbx_builtin_setvar_helper(), RAII_VAR, and send_agent_login().

Referenced by load_module().

02108 {
02109    char *parse;
02110    struct ast_flags opts;
02111    AST_DECLARE_APP_ARGS(args,
02112       AST_APP_ARG(agent_id);
02113       AST_APP_ARG(options);
02114       AST_APP_ARG(other);     /* Any remaining unused arguments */
02115    );
02116 
02117    RAII_VAR(struct agent_pvt *, agent, NULL, ao2_cleanup);
02118 
02119    if (bridge_agent_hold_deferred_create()) {
02120       return -1;
02121    }
02122 
02123    if (ast_channel_state(chan) != AST_STATE_UP && ast_answer(chan)) {
02124       return -1;
02125    }
02126 
02127    parse = ast_strdupa(data);
02128    AST_STANDARD_APP_ARGS(args, parse);
02129 
02130    if (ast_strlen_zero(args.agent_id)) {
02131       ast_log(LOG_WARNING, "AgentLogin requires an AgentId\n");
02132       return -1;
02133    }
02134 
02135    if (ast_app_parse_options(agent_login_opts, &opts, NULL, args.options)) {
02136       /* General invalid option syntax. */
02137       return -1;
02138    }
02139 
02140    /* Find the agent. */
02141    agent = ao2_find(agents, args.agent_id, OBJ_KEY);
02142    if (!agent) {
02143       ast_verb(3, "Agent '%s' does not exist.\n", args.agent_id);
02144       pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "INVALID");
02145       return 0;
02146    }
02147 
02148    /* Has someone already logged in as this agent already? */
02149    agent_lock(agent);
02150    if (agent->logged) {
02151       agent_unlock(agent);
02152       ast_verb(3, "Agent '%s' already logged in.\n", agent->username);
02153       pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "ALREADY_LOGGED_IN");
02154       return 0;
02155    }
02156    agent->logged = ast_channel_ref(chan);
02157    agent->last_disconnect = ast_tvnow();
02158    time(&agent->login_start);
02159    agent->deferred_logoff = 0;
02160    agent_unlock(agent);
02161 
02162    agent_login_channel_config(agent, chan);
02163 
02164    if (!ast_test_flag(&opts, OPT_SILENT)) {
02165       ast_stream_and_wait(chan, "agent-loginok", AST_DIGIT_NONE);
02166    }
02167 
02168    ast_verb(2, "Agent '%s' logged in (format %s/%s)\n", agent->username,
02169       ast_format_get_name(ast_channel_readformat(chan)),
02170       ast_format_get_name(ast_channel_writeformat(chan)));
02171    ast_channel_lock(chan);
02172    send_agent_login(chan, agent->username);
02173    ast_channel_unlock(chan);
02174 
02175    agent_run(agent, chan);
02176    return -1;
02177 }

static int agent_logoff_request ( const char *  agent_id,
int  soft 
) [static]

Definition at line 970 of file app_agent_pool.c.

References agent_lock, agent_lock_logged(), agent_unlock, ao2_cleanup, ao2_find, ast_channel_unlock, ast_channel_unref, ast_softhangup(), AST_SOFTHANGUP_EXPLICIT, OBJ_KEY, and RAII_VAR.

Referenced by action_agent_logoff(), and agent_handle_logoff_cmd().

00971 {
00972    struct ast_channel *logged;
00973    RAII_VAR(struct agent_pvt *, agent, ao2_find(agents, agent_id, OBJ_KEY), ao2_cleanup);
00974 
00975    if (!agent) {
00976       return -1;
00977    }
00978 
00979    agent_lock(agent);
00980    logged = agent_lock_logged(agent);
00981    if (logged) {
00982       if (soft) {
00983          agent->deferred_logoff = 1;
00984       } else {
00985          ast_softhangup(logged, AST_SOFTHANGUP_EXPLICIT);
00986       }
00987       ast_channel_unlock(logged);
00988       ast_channel_unref(logged);
00989    }
00990    agent_unlock(agent);
00991    return 0;
00992 }

static void agent_logout ( struct agent_pvt agent  )  [static]

Definition at line 1476 of file app_agent_pool.c.

References agent_devstate_changed(), AGENT_STATE_LOGGED_OUT, agent_unlock, ast_bridge_destroy(), ast_channel_lock, ast_channel_unlock, ast_channel_unref, ast_clear_flag, AST_DEVICE_UNAVAILABLE, AST_FLAGS_ALL, ast_verb, agent_pvt::caller_bridge, agent_pvt::devstate, agent_pvt::logged, agent_pvt::login_start, NULL, send_agent_logoff(), agent_pvt::state, and agent_pvt::username.

Referenced by agent_after_bridge_cb_failed(), and agent_run().

01477 {
01478    struct ast_channel *logged;
01479    struct ast_bridge *caller_bridge;
01480    long time_logged_in;
01481 
01482    time_logged_in = time(NULL) - agent->login_start;
01483    logged = agent->logged;
01484    agent->logged = NULL;
01485    caller_bridge = agent->caller_bridge;
01486    agent->caller_bridge = NULL;
01487    agent->state = AGENT_STATE_LOGGED_OUT;
01488    agent->devstate = AST_DEVICE_UNAVAILABLE;
01489    ast_clear_flag(agent, AST_FLAGS_ALL);
01490    agent_unlock(agent);
01491    agent_devstate_changed(agent->username);
01492 
01493    if (caller_bridge) {
01494       ast_bridge_destroy(caller_bridge, 0);
01495    }
01496 
01497    ast_channel_lock(logged);
01498    send_agent_logoff(logged, agent->username, time_logged_in);
01499    ast_channel_unlock(logged);
01500    ast_verb(2, "Agent '%s' logged out.  Logged in for %ld seconds.\n",
01501       agent->username, time_logged_in);
01502    ast_channel_unref(logged);
01503 }

static int agent_mark ( void *  obj,
void *  arg,
int  flags 
) [static]

Definition at line 869 of file app_agent_pool.c.

References agent_lock, agent_unlock, and agent_pvt::the_mark.

Referenced by agents_mark().

00870 {
00871    struct agent_pvt *agent = obj;
00872 
00873    agent_lock(agent);
00874    agent->the_mark = 1;
00875    agent_unlock(agent);
00876    return 0;
00877 }

static int agent_pvt_cmp ( void *  obj,
void *  arg,
int  flags 
) [static]

Definition at line 847 of file app_agent_pool.c.

References CMP_MATCH, agent_pvt::logged, OBJ_KEY, OBJ_PARTIAL_KEY, and OBJ_POINTER.

Referenced by load_module().

00848 {
00849    const struct agent_pvt *agent = obj;
00850    int cmp;
00851 
00852    switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
00853    case OBJ_POINTER:
00854    case OBJ_KEY:
00855    case OBJ_PARTIAL_KEY:
00856       cmp = CMP_MATCH;
00857       break;
00858    default:
00859       if (agent->logged == arg) {
00860          cmp = CMP_MATCH;
00861       } else {
00862          cmp = 0;
00863       }
00864       break;
00865    }
00866    return cmp;
00867 }

static void agent_pvt_destructor ( void *  vdoomed  )  [static]

Definition at line 756 of file app_agent_pool.c.

References agent_devstate_changed(), ao2_cleanup, ast_bridge_destroy(), ast_channel_unref, ast_debug, ast_party_connected_line_free(), ast_string_field_free_memory, ast_strlen_zero, agent_pvt::caller_bridge, agent_pvt::cfg, agent_pvt::logged, NULL, agent_pvt::username, and agent_pvt::waiting_colp.

Referenced by agent_pvt_new().

00757 {
00758    struct agent_pvt *doomed = vdoomed;
00759 
00760    /* Make sure device state reflects agent destruction. */
00761    if (!ast_strlen_zero(doomed->username)) {
00762       ast_debug(1, "Agent %s: Destroyed.\n", doomed->username);
00763       agent_devstate_changed(doomed->username);
00764    }
00765 
00766    ast_party_connected_line_free(&doomed->waiting_colp);
00767    if (doomed->caller_bridge) {
00768       ast_bridge_destroy(doomed->caller_bridge, 0);
00769       doomed->caller_bridge = NULL;
00770    }
00771    if (doomed->logged) {
00772       doomed->logged = ast_channel_unref(doomed->logged);
00773    }
00774    ao2_cleanup(doomed->cfg);
00775    doomed->cfg = NULL;
00776    ast_string_field_free_memory(doomed);
00777 }

static enum ast_device_state agent_pvt_devstate_get ( const char *  agent_id  )  [static]

Definition at line 727 of file app_agent_pool.c.

References agent_lock, agent_unlock, ao2_find, ao2_ref, AST_DEVICE_INVALID, agent_pvt::devstate, and OBJ_KEY.

Referenced by load_module().

00728 {
00729    enum ast_device_state dev_state = AST_DEVICE_INVALID;
00730    struct agent_pvt *agent;
00731 
00732    agent = ao2_find(agents, agent_id, OBJ_KEY);
00733    if (agent) {
00734       agent_lock(agent);
00735       dev_state = agent->devstate;
00736       agent_unlock(agent);
00737       ao2_ref(agent, -1);
00738    }
00739    return dev_state;
00740 }

static struct agent_pvt* agent_pvt_new ( struct agent_cfg cfg  )  [static, read]

Definition at line 779 of file app_agent_pool.c.

References agent_pvt_destructor(), ao2_alloc, ao2_ref, AST_DEVICE_UNAVAILABLE, ast_party_connected_line_init(), ast_string_field_init, ast_string_field_set, agent_pvt::cfg, agent_pvt::devstate, NULL, agent_cfg::username, agent_pvt::username, and agent_pvt::waiting_colp.

Referenced by agents_post_apply_config().

00780 {
00781    struct agent_pvt *agent;
00782 
00783    agent = ao2_alloc(sizeof(*agent), agent_pvt_destructor);
00784    if (!agent) {
00785       return NULL;
00786    }
00787    if (ast_string_field_init(agent, 32)) {
00788       ao2_ref(agent, -1);
00789       return NULL;
00790    }
00791    ast_string_field_set(agent, username, cfg->username);
00792    ast_party_connected_line_init(&agent->waiting_colp);
00793    ao2_ref(cfg, +1);
00794    agent->cfg = cfg;
00795    agent->devstate = AST_DEVICE_UNAVAILABLE;
00796    return agent;
00797 }

static int agent_pvt_sort_cmp ( const void *  obj_left,
const void *  obj_right,
int  flags 
) [static]

Definition at line 815 of file app_agent_pool.c.

References OBJ_KEY, OBJ_PARTIAL_KEY, OBJ_POINTER, and agent_pvt::username.

Referenced by load_module().

00816 {
00817    const struct agent_pvt *agent_left = obj_left;
00818    const struct agent_pvt *agent_right = obj_right;
00819    const char *right_key = obj_right;
00820    int cmp;
00821 
00822    switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
00823    default:
00824    case OBJ_POINTER:
00825       right_key = agent_right->username;
00826       /* Fall through */
00827    case OBJ_KEY:
00828       cmp = strcmp(agent_left->username, right_key);
00829       break;
00830    case OBJ_PARTIAL_KEY:
00831       cmp = strncmp(agent_left->username, right_key, strlen(right_key));
00832       break;
00833    }
00834    return cmp;
00835 }

static int agent_request_exec ( struct ast_channel chan,
const char *  data 
) [static]

Dialplan AgentRequest application to locate an agent to talk with.

Parameters:
chan Channel wanting to talk with an agent.
data Application parameters
Return values:
0 To continue in dialplan.
-1 To hangup.

Definition at line 1884 of file app_agent_pool.c.

References __ao2_cleanup(), agent_bridge_channel_get_lock(), agent_devstate_changed(), agent_lock, AGENT_STATE_CALL_PRESENT, AGENT_STATE_LOGGED_OUT, AGENT_STATE_LOGGING_OUT, AGENT_STATE_READY_FOR_CALL, agent_unlock, ao2_cleanup, ao2_find, ao2_ref, args, 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_channel_caller(), ast_channel_flags(), ast_channel_lock, ast_channel_name(), ast_channel_softhangup_internal_flag(), ast_channel_unlock, ast_check_hangup(), ast_connected_line_copy_from_caller(), AST_DECLARE_APP_ARGS, AST_DEVICE_INUSE, AST_FLAG_ZOMBIE, ast_log, ast_party_connected_line_free(), ast_party_connected_line_init(), AST_SOFTHANGUP_ASYNCGOTO, AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero, ast_test_flag, ast_verb, bridge_agent_hold_deferred_create(), caller_abort_agent(), caller_joined_bridge(), caller_safety_timeout(), CALLER_SAFETY_TIMEOUT_TIME, LOG_WARNING, NULL, OBJ_KEY, parse(), pbx_builtin_getvar_helper(), pbx_builtin_setvar_helper(), RAII_VAR, and send_colp_to_agent().

Referenced by load_module().

01885 {
01886    struct ast_bridge *caller_bridge;
01887    struct ast_bridge_channel *logged;
01888    char *parse;
01889    int res;
01890    struct ast_bridge_features caller_features;
01891    struct ast_party_connected_line connected;
01892    AST_DECLARE_APP_ARGS(args,
01893       AST_APP_ARG(agent_id);
01894       AST_APP_ARG(other);     /* Any remaining unused arguments */
01895    );
01896 
01897    RAII_VAR(struct agent_pvt *, agent, NULL, ao2_cleanup);
01898 
01899    if (bridge_agent_hold_deferred_create()) {
01900       return -1;
01901    }
01902 
01903    parse = ast_strdupa(data);
01904    AST_STANDARD_APP_ARGS(args, parse);
01905 
01906    if (ast_strlen_zero(args.agent_id)) {
01907       ast_log(LOG_WARNING, "AgentRequest requires an AgentId\n");
01908       return -1;
01909    }
01910 
01911    /* Find the agent. */
01912    agent = ao2_find(agents, args.agent_id, OBJ_KEY);
01913    if (!agent) {
01914       ast_verb(3, "Agent '%s' does not exist.\n", args.agent_id);
01915       pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "INVALID");
01916       return 0;
01917    }
01918 
01919    if (ast_bridge_features_init(&caller_features)) {
01920       return -1;
01921    }
01922 
01923    /* Add safety timeout hook. */
01924    ao2_ref(agent, +1);
01925    if (ast_bridge_interval_hook(&caller_features, 0, CALLER_SAFETY_TIMEOUT_TIME,
01926       caller_safety_timeout, agent, __ao2_cleanup, AST_BRIDGE_HOOK_REMOVE_ON_PULL)) {
01927       ao2_ref(agent, -1);
01928       ast_bridge_features_cleanup(&caller_features);
01929       return -1;
01930    }
01931 
01932    /* Setup the alert agent on caller joining the bridge hook. */
01933    ao2_ref(agent, +1);
01934    if (ast_bridge_join_hook(&caller_features, caller_joined_bridge, agent,
01935       __ao2_cleanup, 0)) {
01936       ao2_ref(agent, -1);
01937       ast_bridge_features_cleanup(&caller_features);
01938       return -1;
01939    }
01940 
01941    caller_bridge = ast_bridge_basic_new();
01942    if (!caller_bridge) {
01943       ast_bridge_features_cleanup(&caller_features);
01944       return -1;
01945    }
01946 
01947    agent_lock(agent);
01948    switch (agent->state) {
01949    case AGENT_STATE_LOGGED_OUT:
01950    case AGENT_STATE_LOGGING_OUT:
01951       agent_unlock(agent);
01952       ast_bridge_destroy(caller_bridge, 0);
01953       ast_bridge_features_cleanup(&caller_features);
01954       ast_verb(3, "Agent '%s' not logged in.\n", agent->username);
01955       pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "NOT_LOGGED_IN");
01956       return 0;
01957    case AGENT_STATE_READY_FOR_CALL:
01958       ao2_ref(caller_bridge, +1);
01959       agent->caller_bridge = caller_bridge;
01960       agent->state = AGENT_STATE_CALL_PRESENT;
01961       agent->devstate = AST_DEVICE_INUSE;
01962       break;
01963    default:
01964       agent_unlock(agent);
01965       ast_bridge_destroy(caller_bridge, 0);
01966       ast_bridge_features_cleanup(&caller_features);
01967       ast_verb(3, "Agent '%s' is busy.\n", agent->username);
01968       pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "BUSY");
01969       return 0;
01970    }
01971    agent_unlock(agent);
01972    agent_devstate_changed(agent->username);
01973 
01974    /* Get COLP for agent. */
01975    ast_party_connected_line_init(&connected);
01976    ast_channel_lock(chan);
01977    ast_connected_line_copy_from_caller(&connected, ast_channel_caller(chan));
01978    ast_channel_unlock(chan);
01979 
01980    logged = agent_bridge_channel_get_lock(agent);
01981    if (!logged) {
01982       ast_party_connected_line_free(&connected);
01983       caller_abort_agent(agent);
01984       ast_bridge_destroy(caller_bridge, 0);
01985       ast_bridge_features_cleanup(&caller_features);
01986       ast_verb(3, "Agent '%s' not logged in.\n", agent->username);
01987       pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "NOT_LOGGED_IN");
01988       return 0;
01989    }
01990 
01991    send_colp_to_agent(logged, &connected);
01992    ast_bridge_channel_unlock(logged);
01993    ao2_ref(logged, -1);
01994    ast_party_connected_line_free(&connected);
01995 
01996    if (ast_bridge_join(caller_bridge, chan, NULL, &caller_features, NULL,
01997       AST_BRIDGE_JOIN_PASS_REFERENCE)) {
01998       caller_abort_agent(agent);
01999       ast_verb(3, "Agent '%s': Caller %s failed to join the bridge.\n",
02000          agent->username, ast_channel_name(chan));
02001       pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "ERROR");
02002    }
02003    ast_bridge_features_cleanup(&caller_features);
02004 
02005    /* Determine if we need to continue in the dialplan after the bridge. */
02006    ast_channel_lock(chan);
02007    if (ast_channel_softhangup_internal_flag(chan) & AST_SOFTHANGUP_ASYNCGOTO) {
02008       /*
02009        * The bridge was broken for a hangup that isn't real.
02010        * Don't run the h extension, because the channel isn't
02011        * really hung up.  This should really only happen with
02012        * AST_SOFTHANGUP_ASYNCGOTO.
02013        */
02014       res = 0;
02015    } else {
02016       res = ast_check_hangup(chan)
02017          || ast_test_flag(ast_channel_flags(chan), AST_FLAG_ZOMBIE)
02018          || ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENT_STATUS"));
02019    }
02020    ast_channel_unlock(chan);
02021 
02022    return res ? -1 : 0;
02023 }

static void agent_run ( struct agent_pvt agent,
struct ast_channel logged 
) [static]

Definition at line 1515 of file app_agent_pool.c.

References agent_lock, agent_logout(), AGENT_STATE_LOGGING_OUT, agent_unlock, agents_cfg::agents, ao2_find, ao2_global_obj_ref, ao2_ref, ast_bridge_destroy(), ast_bridge_features_cleanup(), ast_bridge_features_init(), ast_bridge_join(), AST_BRIDGE_JOIN_PASS_REFERENCE, AST_CAUSE_NORMAL_CLEARING, ast_channel_hangupcause_set(), ast_channel_name(), ast_channel_update_connected_line(), ast_check_hangup_locked(), ast_debug, ast_tvnow(), agent_pvt::caller_bridge, agent_pvt::cfg, cfg_handle, agent_pvt::dead, agent_pvt::deferred_logoff, agent_pvt::last_disconnect, agent_pvt::logged, NULL, OBJ_KEY, agent_pvt::state, agent_pvt::username, and agent_pvt::waiting_colp.

Referenced by agent_after_bridge_cb(), and agent_login_exec().

01516 {
01517    struct ast_bridge_features features;
01518 
01519    if (ast_bridge_features_init(&features)) {
01520       ast_channel_hangupcause_set(logged, AST_CAUSE_NORMAL_CLEARING);
01521       goto agent_run_cleanup;
01522    }
01523    for (;;) {
01524       struct agents_cfg *cfgs;
01525       struct agent_cfg *cfg_new;
01526       struct agent_cfg *cfg_old;
01527       struct ast_bridge *holding;
01528       struct ast_bridge *caller_bridge;
01529 
01530       ast_channel_hangupcause_set(logged, AST_CAUSE_NORMAL_CLEARING);
01531 
01532       holding = ao2_global_obj_ref(agent_holding);
01533       if (!holding) {
01534          ast_debug(1, "Agent %s: Someone destroyed the agent holding bridge.\n",
01535             agent->username);
01536          break;
01537       }
01538 
01539       /*
01540        * When the agent channel leaves the bridging system we usually
01541        * want to put the agent back into the holding bridge for the
01542        * next caller.
01543        */
01544       ast_bridge_join(holding, logged, NULL, &features, NULL,
01545          AST_BRIDGE_JOIN_PASS_REFERENCE);
01546       if (logged != agent->logged) {
01547          /* This channel is no longer the logged in agent. */
01548          break;
01549       }
01550 
01551       if (agent->dead) {
01552          /* The agent is no longer configured. */
01553          break;
01554       }
01555 
01556       /* Update the agent's config before rejoining the holding bridge. */
01557       cfgs = ao2_global_obj_ref(cfg_handle);
01558       if (!cfgs) {
01559          /* There is no agent configuration.  All agents were destroyed. */
01560          break;
01561       }
01562       cfg_new = ao2_find(cfgs->agents, agent->username, OBJ_KEY);
01563       ao2_ref(cfgs, -1);
01564       if (!cfg_new) {
01565          /* The agent is no longer configured. */
01566          break;
01567       }
01568       agent_lock(agent);
01569       cfg_old = agent->cfg;
01570       agent->cfg = cfg_new;
01571 
01572       agent->last_disconnect = ast_tvnow();
01573 
01574       /* Clear out any caller bridge before rejoining the holding bridge. */
01575       caller_bridge = agent->caller_bridge;
01576       agent->caller_bridge = NULL;
01577       agent_unlock(agent);
01578       ao2_ref(cfg_old, -1);
01579       if (caller_bridge) {
01580          ast_bridge_destroy(caller_bridge, 0);
01581       }
01582 
01583       if (agent->state == AGENT_STATE_LOGGING_OUT
01584          || agent->deferred_logoff
01585          || ast_check_hangup_locked(logged)) {
01586          /* The agent was requested to logout or hungup. */
01587          break;
01588       }
01589 
01590       /*
01591        * It is safe to access agent->waiting_colp without a lock.  It
01592        * is only setup on agent login and not changed.
01593        */
01594       ast_channel_update_connected_line(logged, &agent->waiting_colp, NULL);
01595    }
01596    ast_bridge_features_cleanup(&features);
01597 
01598 agent_run_cleanup:
01599    agent_lock(agent);
01600    if (logged != agent->logged) {
01601       /*
01602        * We are no longer the agent channel because of local channel
01603        * optimization.
01604        */
01605       agent_unlock(agent);
01606       ast_debug(1, "Agent %s: Channel %s is no longer the agent.\n",
01607          agent->username, ast_channel_name(logged));
01608       return;
01609    }
01610    agent_logout(agent);
01611 }

static void agent_show_requested ( struct ast_cli_args a,
int  online_only 
) [static]

Definition at line 2322 of file app_agent_pool.c.

References agent_lock, agent_lock_logged(), agent_unlock, ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_ref, ast_channel_name(), ast_channel_unlock, ast_channel_unref, ast_cli(), ast_devstate_str(), ast_str_alloca, ast_str_buffer(), ast_str_set(), ast_strlen_zero, agent_pvt::cfg, agent_pvt::devstate, ast_cli_args::fd, FORMAT_HDR, FORMAT_ROW, agent_cfg::full_name, out, pbx_builtin_getvar_helper(), and agent_pvt::username.

Referenced by agent_handle_show_all(), and agent_handle_show_online().

02323 {
02324 #define FORMAT_HDR "%-8s %-20s %-11s %-30s %s\n"
02325 #define FORMAT_ROW "%-8s %-20s %-11s %-30s %s\n"
02326 
02327    struct ao2_iterator iter;
02328    struct agent_pvt *agent;
02329    struct ast_str *out = ast_str_alloca(512);
02330    unsigned int agents_total = 0;
02331    unsigned int agents_logged_in = 0;
02332    unsigned int agents_talking = 0;
02333 
02334    ast_cli(a->fd, FORMAT_HDR, "Agent-ID", "Name", "State", "Channel", "Talking with");
02335    iter = ao2_iterator_init(agents, 0);
02336    for (; (agent = ao2_iterator_next(&iter)); ao2_ref(agent, -1)) {
02337       struct ast_channel *logged;
02338 
02339       ++agents_total;
02340 
02341       agent_lock(agent);
02342       logged = agent_lock_logged(agent);
02343       if (logged) {
02344          const char *talking_with;
02345 
02346          ++agents_logged_in;
02347 
02348          talking_with = pbx_builtin_getvar_helper(logged, "BRIDGEPEER");
02349          if (!ast_strlen_zero(talking_with)) {
02350             ++agents_talking;
02351          } else {
02352             talking_with = "";
02353          }
02354          ast_str_set(&out, 0, FORMAT_ROW, agent->username, agent->cfg->full_name,
02355             ast_devstate_str(agent->devstate), ast_channel_name(logged), talking_with);
02356          ast_channel_unlock(logged);
02357          ast_channel_unref(logged);
02358       } else {
02359          ast_str_set(&out, 0, FORMAT_ROW, agent->username, agent->cfg->full_name,
02360             ast_devstate_str(agent->devstate), "", "");
02361       }
02362       agent_unlock(agent);
02363 
02364       if (!online_only || logged) {
02365          ast_cli(a->fd, "%s", ast_str_buffer(out));
02366       }
02367    }
02368    ao2_iterator_destroy(&iter);
02369 
02370    ast_cli(a->fd, "\nDefined agents: %u, Logged in: %u, Talking: %u\n",
02371       agents_total, agents_logged_in, agents_talking);
02372 
02373 #undef FORMAT_HDR
02374 #undef FORMAT_ROW
02375 }

static int agent_sweep ( void *  obj,
void *  arg,
int  flags 
) [static]

Definition at line 884 of file app_agent_pool.c.

References agent_lock, agent_unlock, CMP_MATCH, agent_pvt::dead, and agent_pvt::the_mark.

Referenced by agents_sweep().

00885 {
00886    struct agent_pvt *agent = obj;
00887    int cmp = 0;
00888 
00889    agent_lock(agent);
00890    if (agent->the_mark) {
00891       agent->the_mark = 0;
00892       agent->dead = 1;
00893       /* Unlink dead agents immediately. */
00894       cmp = CMP_MATCH;
00895    }
00896    agent_unlock(agent);
00897    return cmp;
00898 }

static void* agents_cfg_alloc ( void   )  [static]

Definition at line 507 of file app_agent_pool.c.

References agent_cfg_sort_cmp(), agents_cfg::agents, agents_cfg_destructor(), AO2_ALLOC_OPT_LOCK_NOLOCK, ao2_alloc_options, AO2_CONTAINER_ALLOC_OPT_DUPS_REJECT, ao2_container_alloc_rbtree, ao2_ref, and NULL.

00508 {
00509    struct agents_cfg *cfg;
00510 
00511    cfg = ao2_alloc_options(sizeof(*cfg), agents_cfg_destructor,
00512       AO2_ALLOC_OPT_LOCK_NOLOCK);
00513    if (!cfg) {
00514       return NULL;
00515    }
00516    cfg->agents = ao2_container_alloc_rbtree(AO2_ALLOC_OPT_LOCK_NOLOCK,
00517       AO2_CONTAINER_ALLOC_OPT_DUPS_REJECT, agent_cfg_sort_cmp, NULL);
00518    if (!cfg->agents) {
00519       ao2_ref(cfg, -1);
00520       cfg = NULL;
00521    }
00522    return cfg;
00523 }

static void agents_cfg_destructor ( void *  vdoomed  )  [static]

Definition at line 487 of file app_agent_pool.c.

References agents_cfg::agents, ao2_cleanup, and NULL.

Referenced by agents_cfg_alloc().

00488 {
00489    struct agents_cfg *doomed = vdoomed;
00490 
00491    ao2_cleanup(doomed->agents);
00492    doomed->agents = NULL;
00493 }

static void agents_mark ( void   )  [static]

Definition at line 879 of file app_agent_pool.c.

References agent_mark(), ao2_callback, and NULL.

Referenced by agents_post_apply_config().

00880 {
00881    ao2_callback(agents, 0, agent_mark, NULL);
00882 }

static void agents_post_apply_config ( void   )  [static]

Definition at line 930 of file app_agent_pool.c.

References agent_devstate_changed(), agent_lock, agent_pvt_new(), agent_unlock, agents_mark(), agents_sweep(), ao2_cleanup, ao2_find, ao2_global_obj_ref, ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_link, ao2_ref, ast_assert, ast_debug, cfg_handle, NULL, OBJ_KEY, RAII_VAR, and agent_cfg::username.

00931 {
00932    struct ao2_iterator iter;
00933    struct agent_cfg *cfg;
00934    RAII_VAR(struct agents_cfg *, cfgs, ao2_global_obj_ref(cfg_handle), ao2_cleanup);
00935 
00936    ast_assert(cfgs != NULL);
00937 
00938    agents_mark();
00939    iter = ao2_iterator_init(cfgs->agents, 0);
00940    for (; (cfg = ao2_iterator_next(&iter)); ao2_ref(cfg, -1)) {
00941       RAII_VAR(struct agent_pvt *, agent, ao2_find(agents, cfg->username, OBJ_KEY), ao2_cleanup);
00942 
00943       if (agent) {
00944          agent_lock(agent);
00945          agent->the_mark = 0;
00946          if (!agent->logged) {
00947             struct agent_cfg *cfg_old;
00948 
00949             /* Replace the config of agents not logged in. */
00950             cfg_old = agent->cfg;
00951             ao2_ref(cfg, +1);
00952             agent->cfg = cfg;
00953             ao2_cleanup(cfg_old);
00954          }
00955          agent_unlock(agent);
00956          continue;
00957       }
00958       agent = agent_pvt_new(cfg);
00959       if (!agent) {
00960          continue;
00961       }
00962       ao2_link(agents, agent);
00963       ast_debug(1, "Agent %s: Created.\n", agent->username);
00964       agent_devstate_changed(agent->username);
00965    }
00966    ao2_iterator_destroy(&iter);
00967    agents_sweep();
00968 }

static void agents_sweep ( void   )  [static]

Definition at line 900 of file app_agent_pool.c.

References agent_lock, agent_sweep(), agent_unlock, ao2_callback, ao2_iterator_destroy(), ao2_iterator_next, ao2_ref, ast_channel_name(), ast_channel_ref, ast_channel_unref, ast_log, ast_softhangup(), AST_SOFTHANGUP_EXPLICIT, LOG_NOTICE, agent_pvt::logged, NULL, OBJ_MULTIPLE, OBJ_UNLINK, and agent_pvt::username.

Referenced by agents_post_apply_config().

00901 {
00902    struct ao2_iterator *iter;
00903    struct agent_pvt *agent;
00904    struct ast_channel *logged;
00905 
00906    iter = ao2_callback(agents, OBJ_MULTIPLE | OBJ_UNLINK, agent_sweep, NULL);
00907    if (!iter) {
00908       return;
00909    }
00910    for (; (agent = ao2_iterator_next(iter)); ao2_ref(agent, -1)) {
00911       agent_lock(agent);
00912       if (agent->logged) {
00913          logged = ast_channel_ref(agent->logged);
00914       } else {
00915          logged = NULL;
00916       }
00917       agent_unlock(agent);
00918       if (!logged) {
00919          continue;
00920       }
00921       ast_log(LOG_NOTICE,
00922          "Forced logoff of agent %s(%s).  Agent no longer configured.\n",
00923          agent->username, ast_channel_name(logged));
00924       ast_softhangup(logged, AST_SOFTHANGUP_EXPLICIT);
00925       ast_channel_unref(logged);
00926    }
00927    ao2_iterator_destroy(iter);
00928 }

static AO2_GLOBAL_OBJ_STATIC ( agent_holding   )  [static]

Agent holding bridge instance holder.

static AO2_GLOBAL_OBJ_STATIC ( cfg_handle   )  [static]

static int bridge_agent_hold_ack ( struct ast_bridge_channel bridge_channel,
void *  hook_pvt 
) [static]

Definition at line 1080 of file app_agent_pool.c.

References agent_connect_caller(), agent_lock, AGENT_STATE_CALL_WAIT_ACK, agent_unlock, ast_debug, agent_pvt::state, and agent_pvt::username.

Referenced by bridge_agent_hold_push().

01081 {
01082    struct agent_pvt *agent = hook_pvt;
01083 
01084    agent_lock(agent);
01085    switch (agent->state) {
01086    case AGENT_STATE_CALL_WAIT_ACK:
01087       /* Connect to caller now. */
01088       ast_debug(1, "Agent %s: Acked call.\n", agent->username);
01089       agent_connect_caller(bridge_channel, agent);/* Will unlock agent. */
01090       return 0;
01091    default:
01092       break;
01093    }
01094    agent_unlock(agent);
01095    return 0;
01096 }

static int bridge_agent_hold_deferred_create ( void   )  [static]

Definition at line 1414 of file app_agent_pool.c.

References agent_holding_lock, ao2_cleanup, ao2_global_obj_ref, ao2_global_obj_replace_unref, ast_log, ast_mutex_lock, ast_mutex_unlock, bridge_agent_hold_new(), LOG_ERROR, and RAII_VAR.

Referenced by agent_login_exec(), and agent_request_exec().

01415 {
01416    RAII_VAR(struct ast_bridge *, holding, ao2_global_obj_ref(agent_holding), ao2_cleanup);
01417 
01418    if (!holding) {
01419       ast_mutex_lock(&agent_holding_lock);
01420       holding = ao2_global_obj_ref(agent_holding);
01421       if (!holding) {
01422          holding = bridge_agent_hold_new();
01423          ao2_global_obj_replace_unref(agent_holding, holding);
01424       }
01425       ast_mutex_unlock(&agent_holding_lock);
01426       if (!holding) {
01427          ast_log(LOG_ERROR, "Could not create agent holding bridge.\n");
01428          return -1;
01429       }
01430    }
01431    return 0;
01432 }

static void bridge_agent_hold_dissolving ( struct ast_bridge self  )  [static]

The bridge is being dissolved.

Parameters:
self Bridge 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.
Returns:
Nothing

Definition at line 1383 of file app_agent_pool.c.

References ao2_global_obj_release, ast_bridge_base_v_table, and ast_bridge_methods::dissolving.

Referenced by bridge_init_agent_hold().

01384 {
01385    ao2_global_obj_release(agent_holding);
01386    ast_bridge_base_v_table.dissolving(self);
01387 }

static int bridge_agent_hold_heartbeat ( struct ast_bridge_channel bridge_channel,
void *  hook_pvt 
) [static]

Definition at line 1098 of file app_agent_pool.c.

References agent_pvt::ack_time, agent_devstate_changed(), AGENT_FLAG_AUTO_LOGOFF, AGENT_FLAG_WRAPUP_TIME, agent_lock, AGENT_STATE_CALL_WAIT_ACK, AGENT_STATE_CALL_WRAPUP, AGENT_STATE_LOGGING_OUT, AGENT_STATE_PROBATION_WAIT, AGENT_STATE_READY_FOR_CALL, agent_unlock, ast_bridge_channel_leave_bridge(), AST_CAUSE_NORMAL_CLEARING, ast_debug, AST_DEVICE_NOT_INUSE, ast_test_flag, ast_tvdiff_ms(), ast_tvnow(), agent_cfg::auto_logoff, BRIDGE_CHANNEL_STATE_END, agent_pvt::cfg, agent_pvt::deferred_logoff, agent_pvt::devstate, agent_pvt::last_disconnect, LOGIN_WAIT_TIMEOUT_TIME, NULL, agent_pvt::override_auto_logoff, agent_pvt::override_wrapup_time, agent_pvt::probation_start, agent_pvt::state, agent_pvt::username, and agent_cfg::wrapup_time.

Referenced by bridge_agent_hold_push().

01099 {
01100    struct agent_pvt *agent = hook_pvt;
01101    int probation_timedout = 0;
01102    int ack_timedout = 0;
01103    int wrapup_timedout = 0;
01104    int deferred_logoff;
01105    unsigned int wrapup_time;
01106    unsigned int auto_logoff;
01107 
01108    agent_lock(agent);
01109    deferred_logoff = agent->deferred_logoff;
01110    if (deferred_logoff) {
01111       agent->state = AGENT_STATE_LOGGING_OUT;
01112    }
01113 
01114    switch (agent->state) {
01115    case AGENT_STATE_PROBATION_WAIT:
01116       probation_timedout =
01117          LOGIN_WAIT_TIMEOUT_TIME <= (time(NULL) - agent->probation_start);
01118       if (probation_timedout) {
01119          /* Now ready for a caller. */
01120          agent->state = AGENT_STATE_READY_FOR_CALL;
01121          agent->devstate = AST_DEVICE_NOT_INUSE;
01122       }
01123       break;
01124    case AGENT_STATE_CALL_WAIT_ACK:
01125       /* Check ack call time. */
01126       auto_logoff = agent->cfg->auto_logoff;
01127       if (ast_test_flag(agent, AGENT_FLAG_AUTO_LOGOFF)) {
01128          auto_logoff = agent->override_auto_logoff;
01129       }
01130       if (auto_logoff) {
01131          auto_logoff *= 1000;
01132          ack_timedout = ast_tvdiff_ms(ast_tvnow(), agent->ack_time) > auto_logoff;
01133          if (ack_timedout) {
01134             agent->state = AGENT_STATE_LOGGING_OUT;
01135          }
01136       }
01137       break;
01138    case AGENT_STATE_CALL_WRAPUP:
01139       /* Check wrapup time. */
01140       wrapup_time = agent->cfg->wrapup_time;
01141       if (ast_test_flag(agent, AGENT_FLAG_WRAPUP_TIME)) {
01142          wrapup_time = agent->override_wrapup_time;
01143       }
01144       wrapup_timedout = ast_tvdiff_ms(ast_tvnow(), agent->last_disconnect) > wrapup_time;
01145       if (wrapup_timedout) {
01146          agent->state = AGENT_STATE_READY_FOR_CALL;
01147          agent->devstate = AST_DEVICE_NOT_INUSE;
01148       }
01149       break;
01150    default:
01151       break;
01152    }
01153    agent_unlock(agent);
01154 
01155    if (deferred_logoff) {
01156       ast_debug(1, "Agent %s: Deferred logoff.\n", agent->username);
01157       ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END,
01158          AST_CAUSE_NORMAL_CLEARING);
01159    } else if (probation_timedout) {
01160       ast_debug(1, "Agent %s: Login complete.\n", agent->username);
01161       agent_devstate_changed(agent->username);
01162    } else if (ack_timedout) {
01163       ast_debug(1, "Agent %s: Ack call timeout.\n", agent->username);
01164       ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END,
01165          AST_CAUSE_NORMAL_CLEARING);
01166    } else if (wrapup_timedout) {
01167       ast_debug(1, "Agent %s: Wrapup timeout. Ready for new call.\n", agent->username);
01168       agent_devstate_changed(agent->username);
01169    }
01170 
01171    return 0;
01172 }

static struct ast_bridge* bridge_agent_hold_new ( void   )  [static, read]

static void bridge_agent_hold_pull ( struct ast_bridge self,
struct ast_bridge_channel bridge_channel 
) [static]

Definition at line 1364 of file app_agent_pool.c.

References ast_bridge_base_v_table, ast_channel_remove_bridge_role(), ast_bridge_channel::chan, and ast_bridge_methods::pull.

Referenced by bridge_init_agent_hold().

01365 {
01366    ast_channel_remove_bridge_role(bridge_channel->chan, "holding_participant");
01367    ast_bridge_base_v_table.pull(self, bridge_channel);
01368 }

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 1191 of file app_agent_pool.c.

References __ao2_cleanup(), agent_after_bridge_cb(), agent_after_bridge_cb_failed(), agent_devstate_changed(), AGENT_FLAG_ACK_CALL, AGENT_FLAG_DTMF_ACCEPT, AGENT_FLAG_WRAPUP_TIME, 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_cleanup, ao2_find, ao2_ref, ast_assert, 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_CAUSE_NORMAL_CLEARING, 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_FEATURE_MAX_LEN, ast_strdupa, ast_strlen_zero, ast_test_flag, bridge_agent_hold_ack(), bridge_agent_hold_heartbeat(), BRIDGE_CHANNEL_STATE_END, ast_bridge_channel::chan, ast_bridge_channel::features, NULL, ast_bridge_methods::push, and RAII_VAR.

Referenced by bridge_init_agent_hold().

01192 {
01193    int res = 0;
01194    unsigned int wrapup_time;
01195    char dtmf[AST_FEATURE_MAX_LEN];
01196    struct ast_channel *chan;
01197    const char *moh_class;
01198    RAII_VAR(struct agent_pvt *, agent, NULL, ao2_cleanup);
01199 
01200    chan = bridge_channel->chan;
01201 
01202    agent = ao2_find(agents, swap ? swap->chan : chan, 0);
01203    if (!agent) {
01204       /* Could not find the agent. */
01205       return -1;
01206    }
01207 
01208    /* Setup agent entertainment */
01209    agent_lock(agent);
01210    moh_class = ast_strdupa(agent->cfg->moh);
01211    agent_unlock(agent);
01212    res |= ast_channel_add_bridge_role(chan, "holding_participant");
01213    res |= ast_channel_set_bridge_role_option(chan, "holding_participant", "idle_mode", "musiconhold");
01214    res |= ast_channel_set_bridge_role_option(chan, "holding_participant", "moh_class", moh_class);
01215 
01216    /* Add DTMF acknowledge hook. */
01217    dtmf[0] = '\0';
01218    agent_lock(agent);
01219    if (ast_test_flag(agent, AGENT_FLAG_ACK_CALL)
01220       ? agent->override_ack_call : agent->cfg->ack_call) {
01221       const char *dtmf_accept;
01222 
01223       dtmf_accept = ast_test_flag(agent, AGENT_FLAG_DTMF_ACCEPT)
01224          ? agent->override_dtmf_accept : agent->cfg->dtmf_accept;
01225       ast_copy_string(dtmf, dtmf_accept, sizeof(dtmf));
01226    }
01227    agent_unlock(agent);
01228    if (!ast_strlen_zero(dtmf)) {
01229       ao2_ref(agent, +1);
01230       if (ast_bridge_dtmf_hook(bridge_channel->features, dtmf, bridge_agent_hold_ack,
01231          agent, __ao2_cleanup, AST_BRIDGE_HOOK_REMOVE_ON_PULL)) {
01232          ao2_ref(agent, -1);
01233          res = -1;
01234       }
01235    }
01236 
01237    /* Add heartbeat interval hook. */
01238    ao2_ref(agent, +1);
01239    if (ast_bridge_interval_hook(bridge_channel->features, 0, 1000,
01240       bridge_agent_hold_heartbeat, agent, __ao2_cleanup, AST_BRIDGE_HOOK_REMOVE_ON_PULL)) {
01241       ao2_ref(agent, -1);
01242       res = -1;
01243    }
01244 
01245    res |= ast_bridge_base_v_table.push(self, bridge_channel, swap);
01246    if (res) {
01247       ast_channel_remove_bridge_role(chan, "holding_participant");
01248       return -1;
01249    }
01250 
01251    if (swap) {
01252       res = ast_bridge_set_after_callback(chan, agent_after_bridge_cb,
01253          agent_after_bridge_cb_failed, chan);
01254       if (res) {
01255          ast_channel_remove_bridge_role(chan, "holding_participant");
01256          return -1;
01257       }
01258 
01259       agent_lock(agent);
01260       ast_channel_unref(agent->logged);
01261       agent->logged = ast_channel_ref(chan);
01262       agent_unlock(agent);
01263 
01264       /*
01265        * Kick the channel out so it can come back in fully controlled.
01266        * Otherwise, the after bridge callback will linger and the
01267        * agent will have some slightly different behavior in corner
01268        * cases.
01269        */
01270       ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END,
01271          AST_CAUSE_NORMAL_CLEARING);
01272       return 0;
01273    }
01274 
01275    agent_lock(agent);
01276    switch (agent->state) {
01277    case AGENT_STATE_LOGGED_OUT:
01278       /*!
01279        * \todo XXX the login probation time should be only if it is needed.
01280        *
01281        * Need to determine if there are any local channels that can
01282        * optimize and wait until they actually do before leaving the
01283        * AGENT_STATE_PROBATION_WAIT state.  For now, the blind
01284        * timer of LOGIN_WAIT_TIMEOUT_TIME will do.
01285        */
01286       /*
01287        * Start the login probation timer.
01288        *
01289        * We cannot handle an agent local channel optimization when the
01290        * agent is on a call.  The optimization may kick the agent
01291        * channel we know about out of the call without our being able
01292        * to switch to the replacement channel.  Get any agent local
01293        * channel optimization out of the way while the agent is in the
01294        * holding bridge.
01295        */
01296       time(&agent->probation_start);
01297       agent->state = AGENT_STATE_PROBATION_WAIT;
01298       agent_unlock(agent);
01299       break;
01300    case AGENT_STATE_PROBATION_WAIT:
01301       /* Restart the probation timer. */
01302       time(&agent->probation_start);
01303       agent_unlock(agent);
01304       break;
01305    case AGENT_STATE_READY_FOR_CALL:
01306       /*
01307        * Likely someone manually kicked us out of the holding bridge
01308        * and we came right back in.
01309        */
01310       agent_unlock(agent);
01311       break;
01312    default:
01313       /* Unexpected agent state. */
01314       ast_assert(0);
01315       /* Fall through */
01316    case AGENT_STATE_CALL_PRESENT:
01317    case AGENT_STATE_CALL_WAIT_ACK:
01318       agent->state = AGENT_STATE_READY_FOR_CALL;
01319       agent->devstate = AST_DEVICE_NOT_INUSE;
01320       agent_unlock(agent);
01321       ast_debug(1, "Agent %s: Call abort recovery complete.\n", agent->username);
01322       agent_devstate_changed(agent->username);
01323       break;
01324    case AGENT_STATE_ON_CALL:
01325    case AGENT_STATE_CALL_WRAPUP:
01326       wrapup_time = agent->cfg->wrapup_time;
01327       if (ast_test_flag(agent, AGENT_FLAG_WRAPUP_TIME)) {
01328          wrapup_time = agent->override_wrapup_time;
01329       }
01330       if (wrapup_time) {
01331          agent->state = AGENT_STATE_CALL_WRAPUP;
01332       } else {
01333          agent->state = AGENT_STATE_READY_FOR_CALL;
01334          agent->devstate = AST_DEVICE_NOT_INUSE;
01335       }
01336       agent_unlock(agent);
01337       if (!wrapup_time) {
01338          /* No wrapup time. */
01339          ast_debug(1, "Agent %s: Ready for new call.\n", agent->username);
01340          agent_devstate_changed(agent->username);
01341       }
01342       break;
01343    }
01344 
01345    return 0;
01346 }

static void bridge_init_agent_hold ( void   )  [static]

static void caller_abort_agent ( struct agent_pvt agent  )  [static]

Definition at line 1691 of file app_agent_pool.c.

References agent_bridge_channel_get_lock(), agent_lock, agent_unlock, ast_bridge_channel_leave_bridge_nolock(), ast_bridge_channel_unlock, ast_bridge_destroy(), AST_CAUSE_NORMAL_CLEARING, ast_debug, BRIDGE_CHANNEL_STATE_END, agent_pvt::caller_bridge, NULL, and agent_pvt::username.

Referenced by agent_request_exec(), caller_joined_bridge(), and caller_safety_timeout().

01692 {
01693    struct ast_bridge_channel *logged;
01694 
01695    logged = agent_bridge_channel_get_lock(agent);
01696    if (!logged) {
01697       struct ast_bridge *caller_bridge;
01698 
01699       ast_debug(1, "Agent '%s' no longer logged in.\n", agent->username);
01700 
01701       agent_lock(agent);
01702       caller_bridge = agent->caller_bridge;
01703       agent->caller_bridge = NULL;
01704       agent_unlock(agent);
01705       if (caller_bridge) {
01706          ast_bridge_destroy(caller_bridge, 0);
01707       }
01708       return;
01709    }
01710 
01711    /* Kick the agent out of the holding bridge to reset it. */
01712    ast_bridge_channel_leave_bridge_nolock(logged, BRIDGE_CHANNEL_STATE_END,
01713       AST_CAUSE_NORMAL_CLEARING);
01714    ast_bridge_channel_unlock(logged);
01715 }

static int caller_joined_bridge ( struct ast_bridge_channel bridge_channel,
void *  hook_pvt 
) [static]

Definition at line 1842 of file app_agent_pool.c.

References agent_bridge_channel_get_lock(), ao2_ref, ast_bridge_channel_leave_bridge(), ast_bridge_channel_unlock, AST_CONTROL_RINGING, ast_indicate(), ast_verb, BRIDGE_CHANNEL_STATE_END, caller_abort_agent(), ast_bridge_channel::chan, pbx_builtin_setvar_helper(), send_alert_to_agent(), and agent_pvt::username.

Referenced by agent_request_exec().

01843 {
01844    struct agent_pvt *agent = hook_pvt;
01845    struct ast_bridge_channel *logged;
01846    int res;
01847 
01848    logged = agent_bridge_channel_get_lock(agent);
01849    if (!logged) {
01850       ast_verb(3, "Agent '%s' not logged in.\n", agent->username);
01851       pbx_builtin_setvar_helper(bridge_channel->chan, "AGENT_STATUS", "NOT_LOGGED_IN");
01852 
01853       ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END, 0);
01854       caller_abort_agent(agent);
01855       return -1;
01856    }
01857 
01858    res = send_alert_to_agent(logged, agent->username);
01859    ast_bridge_channel_unlock(logged);
01860    ao2_ref(logged, -1);
01861    if (res) {
01862       ast_verb(3, "Agent '%s': Failed to alert the agent.\n", agent->username);
01863       pbx_builtin_setvar_helper(bridge_channel->chan, "AGENT_STATUS", "ERROR");
01864 
01865       ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END, 0);
01866       caller_abort_agent(agent);
01867       return -1;
01868    }
01869 
01870    pbx_builtin_setvar_helper(bridge_channel->chan, "AGENT_STATUS", "NOT_CONNECTED");
01871    ast_indicate(bridge_channel->chan, AST_CONTROL_RINGING);
01872    return -1;
01873 }

static int caller_safety_timeout ( struct ast_bridge_channel bridge_channel,
void *  hook_pvt 
) [static]

Definition at line 1717 of file app_agent_pool.c.

References AGENT_STATE_CALL_PRESENT, ast_bridge_channel_leave_bridge(), ast_log, BRIDGE_CHANNEL_STATE_END, caller_abort_agent(), ast_bridge_channel::chan, LOG_WARNING, pbx_builtin_setvar_helper(), agent_pvt::state, and agent_pvt::username.

Referenced by agent_request_exec().

01718 {
01719    struct agent_pvt *agent = hook_pvt;
01720 
01721    if (agent->state == AGENT_STATE_CALL_PRESENT) {
01722       ast_log(LOG_WARNING, "Agent '%s' process did not respond.  Safety timeout.\n",
01723          agent->username);
01724       pbx_builtin_setvar_helper(bridge_channel->chan, "AGENT_STATUS", "ERROR");
01725 
01726       ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END, 0);
01727       caller_abort_agent(agent);
01728    }
01729 
01730    return -1;
01731 }

static void clear_agent_status ( struct ast_bridge_channel bridge_channel,
const void *  payload,
size_t  payload_size 
) [static]

Definition at line 1012 of file app_agent_pool.c.

References ast_bridge_channel::chan, NULL, and pbx_builtin_setvar_helper().

Referenced by agent_connect_caller().

01013 {
01014    pbx_builtin_setvar_helper(bridge_channel->chan, "AGENT_STATUS", NULL);
01015 }

static char* complete_agent ( const char *  word,
int  state 
) [static]

Definition at line 2272 of file app_agent_pool.c.

References ao2_callback_data, ao2_ref, ast_strdup, ast_strlen_zero, complete_agent_search(), NULL, OBJ_PARTIAL_KEY, agent_complete::state, and agent_pvt::username.

Referenced by agent_handle_show_specific().

02273 {
02274    char *ret;
02275    struct agent_pvt *agent;
02276    struct agent_complete search = {
02277       .state = state,
02278    };
02279 
02280    agent = ao2_callback_data(agents, ast_strlen_zero(word) ? 0 : OBJ_PARTIAL_KEY,
02281       complete_agent_search, (char *) word, &search);
02282    if (!agent) {
02283       return NULL;
02284    }
02285    ret = ast_strdup(agent->username);
02286    ao2_ref(agent, -1);
02287    return ret;
02288 }

static char* complete_agent_logoff ( const char *  word,
int  state 
) [static]

Definition at line 2304 of file app_agent_pool.c.

References ao2_callback_data, ao2_ref, ast_strdup, ast_strlen_zero, complete_agent_logoff_search(), NULL, OBJ_PARTIAL_KEY, agent_complete::state, and agent_pvt::username.

Referenced by agent_handle_logoff_cmd().

02305 {
02306    char *ret;
02307    struct agent_pvt *agent;
02308    struct agent_complete search = {
02309       .state = state,
02310    };
02311 
02312    agent = ao2_callback_data(agents, ast_strlen_zero(word) ? 0 : OBJ_PARTIAL_KEY,
02313       complete_agent_logoff_search, (char *) word, &search);
02314    if (!agent) {
02315       return NULL;
02316    }
02317    ret = ast_strdup(agent->username);
02318    ao2_ref(agent, -1);
02319    return ret;
02320 }

static int complete_agent_logoff_search ( void *  obj,
void *  arg,
void *  data,
int  flags 
) [static]

Definition at line 2290 of file app_agent_pool.c.

References CMP_MATCH, agent_pvt::logged, agent_complete::state, and agent_complete::which.

Referenced by complete_agent_logoff().

02291 {
02292    struct agent_pvt *agent = obj;
02293    struct agent_complete *search = data;
02294 
02295    if (!agent->logged) {
02296       return 0;
02297    }
02298    if (++search->which > search->state) {
02299       return CMP_MATCH;
02300    }
02301    return 0;
02302 }

static int complete_agent_search ( void *  obj,
void *  arg,
void *  data,
int  flags 
) [static]

Definition at line 2262 of file app_agent_pool.c.

References CMP_MATCH, agent_complete::state, and agent_complete::which.

Referenced by complete_agent().

02263 {
02264    struct agent_complete *search = data;
02265 
02266    if (++search->which > search->state) {
02267       return CMP_MATCH;
02268    }
02269    return 0;
02270 }

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]

Definition at line 532 of file app_agent_pool.c.

References aco_info_destroy(), ao2_global_obj_release, and cfg_handle.

Referenced by unload_module().

00533 {
00534    ao2_global_obj_release(cfg_handle);
00535    aco_info_destroy(&cfg_info);
00536 }

static int load_config ( void   )  [static]

Definition at line 538 of file app_agent_pool.c.

References ACO_EXACT, aco_info_init(), aco_option_register, aco_process_config(), ACO_PROCESS_ERROR, FLDSET, NULL, OPT_BOOL_T, OPT_STRINGFIELD_T, OPT_UINT_T, and STRFLDSET.

Referenced by __reload(), ast_features_config_init(), handle_voicemail_reload(), load_module(), main(), reload(), and reload_module().

00539 {
00540    if (aco_info_init(&cfg_info)) {
00541       return -1;
00542    }
00543 
00544    /* Agent options */
00545    aco_option_register(&cfg_info, "ackcall", ACO_EXACT, agent_types, "no", OPT_BOOL_T, 1, FLDSET(struct agent_cfg, ack_call));
00546    aco_option_register(&cfg_info, "acceptdtmf", ACO_EXACT, agent_types, "#", OPT_STRINGFIELD_T, 1, STRFLDSET(struct agent_cfg, dtmf_accept));
00547    aco_option_register(&cfg_info, "autologoff", ACO_EXACT, agent_types, "0", OPT_UINT_T, 0, FLDSET(struct agent_cfg, auto_logoff));
00548    aco_option_register(&cfg_info, "wrapuptime", ACO_EXACT, agent_types, "0", OPT_UINT_T, 0, FLDSET(struct agent_cfg, wrapup_time));
00549    aco_option_register(&cfg_info, "musiconhold", ACO_EXACT, agent_types, "default", OPT_STRINGFIELD_T, 0, STRFLDSET(struct agent_cfg, moh));
00550    aco_option_register(&cfg_info, "recordagentcalls", ACO_EXACT, agent_types, "no", OPT_BOOL_T, 1, FLDSET(struct agent_cfg, record_agent_calls));
00551    aco_option_register(&cfg_info, "custom_beep", ACO_EXACT, agent_types, "beep", OPT_STRINGFIELD_T, 0, STRFLDSET(struct agent_cfg, beep_sound));
00552    aco_option_register(&cfg_info, "fullname", ACO_EXACT, agent_types, NULL, OPT_STRINGFIELD_T, 0, STRFLDSET(struct agent_cfg, full_name));
00553 
00554    if (aco_process_config(&cfg_info, 0) == ACO_PROCESS_ERROR) {
00555       return -1;
00556    }
00557 
00558    return 0;
00559 }

static int load_module ( void   )  [static]

Definition at line 2651 of file app_agent_pool.c.

References action_agent_logoff(), action_agents(), agent_login_exec(), agent_pvt_cmp(), agent_pvt_devstate_get(), agent_pvt_sort_cmp(), agent_request_exec(), AO2_ALLOC_OPT_LOCK_MUTEX, AO2_CONTAINER_ALLOC_OPT_DUPS_REPLACE, ao2_container_alloc_rbtree, ARRAY_LEN, ast_cli_register_multiple(), ast_custom_function_register, ast_devstate_prov_add(), ast_log, ast_manager_register_xml, AST_MODULE_LOAD_DECLINE, AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_SUCCESS, ast_register_application_xml, bridge_init_agent_hold(), EVENT_FLAG_AGENT, load_config(), LOG_ERROR, and unload_module.

02652 {
02653    int res = 0;
02654 
02655    agents = ao2_container_alloc_rbtree(AO2_ALLOC_OPT_LOCK_MUTEX,
02656       AO2_CONTAINER_ALLOC_OPT_DUPS_REPLACE, agent_pvt_sort_cmp, agent_pvt_cmp);
02657    if (!agents) {
02658       return AST_MODULE_LOAD_FAILURE;
02659    }
02660 
02661    /* Init agent holding bridge v_table. */
02662    bridge_init_agent_hold();
02663 
02664    /* Setup to provide Agent:agent-id device state. */
02665    res |= ast_devstate_prov_add("Agent", agent_pvt_devstate_get);
02666 
02667    /* CLI Commands */
02668    res |= ast_cli_register_multiple(cli_agents, ARRAY_LEN(cli_agents));
02669 
02670    /* Manager commands */
02671    res |= ast_manager_register_xml("Agents", EVENT_FLAG_AGENT, action_agents);
02672    res |= ast_manager_register_xml("AgentLogoff", EVENT_FLAG_AGENT, action_agent_logoff);
02673 
02674    /* Dialplan Functions */
02675    res |= ast_custom_function_register(&agent_function);
02676 
02677    /* Dialplan applications */
02678    res |= ast_register_application_xml(app_agent_login, agent_login_exec);
02679    res |= ast_register_application_xml(app_agent_request, agent_request_exec);
02680 
02681    if (res) {
02682       unload_module();
02683       return AST_MODULE_LOAD_FAILURE;
02684    }
02685 
02686    if (load_config()) {
02687       ast_log(LOG_ERROR, "Unable to load config. Not loading module.\n");
02688       unload_module();
02689       return AST_MODULE_LOAD_DECLINE;
02690    }
02691 
02692    return AST_MODULE_LOAD_SUCCESS;
02693 }

static int reload ( void   )  [static]

Definition at line 2695 of file app_agent_pool.c.

References aco_process_config(), and ACO_PROCESS_ERROR.

02696 {
02697    if (aco_process_config(&cfg_info, 1) == ACO_PROCESS_ERROR) {
02698       /* Just keep the config we already have in place. */
02699       return -1;
02700    }
02701    return 0;
02702 }

static void send_agent_login ( struct ast_channel chan,
const char *  agent 
) [static]

Definition at line 1434 of file app_agent_pool.c.

References ast_assert, ast_channel_agent_login_type(), ast_channel_publish_cached_blob(), ast_json_pack(), ast_json_unref(), NULL, and RAII_VAR.

Referenced by agent_login_exec().

01435 {
01436    RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
01437 
01438    ast_assert(agent != NULL);
01439 
01440    blob = ast_json_pack("{s: s}",
01441       "agent", agent);
01442    if (!blob) {
01443       return;
01444    }
01445 
01446    ast_channel_publish_cached_blob(chan, ast_channel_agent_login_type(), blob);
01447 }

static void send_agent_logoff ( struct ast_channel chan,
const char *  agent,
long  logintime 
) [static]

Definition at line 1449 of file app_agent_pool.c.

References ast_assert, ast_channel_agent_logoff_type(), ast_channel_publish_cached_blob(), ast_json_pack(), ast_json_unref(), NULL, and RAII_VAR.

Referenced by agent_logout().

01450 {
01451    RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
01452 
01453    ast_assert(agent != NULL);
01454 
01455    blob = ast_json_pack("{s: s, s: i}",
01456       "agent", agent,
01457       "logintime", logintime);
01458    if (!blob) {
01459       return;
01460    }
01461 
01462    ast_channel_publish_cached_blob(chan, ast_channel_agent_logoff_type(), blob);
01463 }

static int send_alert_to_agent ( struct ast_bridge_channel bridge_channel,
const char *  agent_id 
) [static]

Definition at line 1807 of file app_agent_pool.c.

References agent_alert(), AST_BRIDGE_CHANNEL_CB_OPTION_MEDIA, and ast_bridge_channel_queue_callback().

Referenced by caller_joined_bridge().

01808 {
01809    return ast_bridge_channel_queue_callback(bridge_channel,
01810       AST_BRIDGE_CHANNEL_CB_OPTION_MEDIA, agent_alert, agent_id, strlen(agent_id) + 1);
01811 }

static int send_colp_to_agent ( struct ast_bridge_channel bridge_channel,
struct ast_party_connected_line connected 
) [static]

Definition at line 1813 of file app_agent_pool.c.

References ast_bridge_channel_queue_control_data(), ast_connected_line_build_data(), AST_CONTROL_CONNECTED_LINE, ast_set_party_connected_line::id, and ast_set_party_id::name.

Referenced by agent_request_exec().

01814 {
01815    struct ast_set_party_connected_line update = {
01816       .id.name = 1,
01817       .id.number = 1,
01818       .id.subaddress = 1,
01819    };
01820    unsigned char data[1024];  /* This should be large enough */
01821    size_t datalen;
01822 
01823    datalen = ast_connected_line_build_data(data, sizeof(data), connected, &update);
01824    if (datalen == (size_t) -1) {
01825       return 0;
01826    }
01827 
01828    return ast_bridge_channel_queue_control_data(bridge_channel,
01829       AST_CONTROL_CONNECTED_LINE, data, datalen);
01830 }

static int unload_module ( void   )  [static]

Definition at line 2619 of file app_agent_pool.c.

References ao2_cleanup, ao2_global_obj_replace, ARRAY_LEN, ast_bridge_destroy(), ast_cli_unregister_multiple(), ast_custom_function_unregister(), ast_devstate_prov_del(), ast_manager_unregister(), ast_unregister_application(), destroy_config(), and NULL.

02620 {
02621    struct ast_bridge *holding;
02622 
02623    /* Unregister dialplan applications */
02624    ast_unregister_application(app_agent_login);
02625    ast_unregister_application(app_agent_request);
02626 
02627    /* Unregister dialplan functions */
02628    ast_custom_function_unregister(&agent_function);
02629 
02630    /* Unregister manager command */
02631    ast_manager_unregister("Agents");
02632    ast_manager_unregister("AgentLogoff");
02633 
02634    /* Unregister CLI commands */
02635    ast_cli_unregister_multiple(cli_agents, ARRAY_LEN(cli_agents));
02636 
02637    ast_devstate_prov_del("Agent");
02638 
02639    /* Destroy agent holding bridge. */
02640    holding = ao2_global_obj_replace(agent_holding, NULL);
02641    if (holding) {
02642       ast_bridge_destroy(holding, 0);
02643    }
02644 
02645    destroy_config();
02646    ao2_cleanup(agents);
02647    agents = NULL;
02648    return 0;
02649 }


Variable Documentation

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 = AST_BUILDOPT_SUM, .support_level = AST_MODULE_SUPPORT_CORE, .load = load_module, .unload = unload_module, .reload = reload, .load_pri = AST_MODPRI_DEVSTATE_PROVIDER, } [static]

Definition at line 2710 of file app_agent_pool.c.

Initial value:

 {
   .name = "AGENT",
   .read = agent_function_read,
}

Definition at line 2250 of file app_agent_pool.c.

ast_mutex_t agent_holding_lock = { PTHREAD_MUTEX_INITIALIZER , NULL, 1 } [static]

Agent holding bridge deferred creation lock.

Definition at line 998 of file app_agent_pool.c.

Referenced by bridge_agent_hold_deferred_create().

struct ast_app_option agent_login_opts[128] = { [ 's' ] = { .flag = OPT_SILENT }, } [static]

Definition at line 2096 of file app_agent_pool.c.

Referenced by agent_login_exec().

struct aco_type agent_type [static]

Definition at line 460 of file app_agent_pool.c.

struct aco_type* agent_types[] = ACO_TYPES(&agent_type) [static]

Definition at line 470 of file app_agent_pool.c.

struct ao2_container* agents [static]

Container of defined agents.

Definition at line 644 of file app_agent_pool.c.

struct aco_file agents_conf [static]

Initial value:

 {
   .filename = "agents.conf",
   .types = ACO_TYPES(&general_type, &agent_type),
}

Definition at line 480 of file app_agent_pool.c.

const char app_agent_login[] = "AgentLogin" [static]

Definition at line 346 of file app_agent_pool.c.

const char app_agent_request[] = "AgentRequest" [static]

Definition at line 347 of file app_agent_pool.c.

Definition at line 2710 of file app_agent_pool.c.

Definition at line 1389 of file app_agent_pool.c.

Referenced by bridge_agent_hold_new(), and bridge_init_agent_hold().

struct ast_cli_entry cli_agents[] [static]

Definition at line 2515 of file app_agent_pool.c.

struct aco_type general_type [static]

Definition at line 473 of file app_agent_pool.c.


Generated on Thu Apr 16 06:28:17 2015 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6