core_local.c File Reference

Local proxy channel driver. More...

#include "asterisk.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/cli.h"
#include "asterisk/manager.h"
#include "asterisk/devicestate.h"
#include "asterisk/astobj2.h"
#include "asterisk/bridge.h"
#include "asterisk/core_unreal.h"
#include "asterisk/core_local.h"
#include "asterisk/stasis.h"
#include "asterisk/stasis_channels.h"
#include "asterisk/_private.h"

Include dependency graph for core_local.c:

Go to the source code of this file.

Data Structures

struct  local_bridge
struct  local_pvt
 the local pvt structure for all channels More...

Enumerations

enum  local_call_action { LOCAL_CALL_ACTION_DIALPLAN, LOCAL_CALL_ACTION_BRIDGE, LOCAL_CALL_ACTION_MASQUERADE }

Functions

struct ast_channelast_local_get_peer (struct ast_channel *ast)
 Get the other local channel in the pair.
int ast_local_init (void)
 Initialize the local proxy channel.
int ast_local_setup_bridge (struct ast_channel *ast, struct ast_bridge *bridge, struct ast_channel *swap, struct ast_bridge_features *features)
 Setup the outgoing local channel to join a bridge on ast_call().
int ast_local_setup_masquerade (struct ast_channel *ast, struct ast_channel *masq)
 Setup the outgoing local channel to masquerade into a channel on ast_call().
static struct local_pvtlocal_alloc (const char *data, struct ast_format_cap *cap)
 Create a call structure.
static int local_call (struct ast_channel *ast, const char *dest, int timeout)
 Initiate new call, part of PBX interface dest is the dial string.
static struct
ast_multi_channel_blob
local_channel_optimization_blob (struct local_pvt *p, struct ast_json *json_object)
static int local_devicestate (const char *data)
 Adds devicestate to local channels.
static int local_hangup (struct ast_channel *ast)
 Hangup a call through the local proxy channel.
static struct
ast_manager_event_blob
local_message_to_ami (struct stasis_message *msg)
static void local_optimization_finished_cb (struct ast_unreal_pvt *base, int success, unsigned int id)
 Callback for ast_unreal_pvt_callbacks optimization_finished_cb.
static void local_optimization_started_cb (struct ast_unreal_pvt *base, struct ast_channel *source, enum ast_unreal_channel_indicator dest, unsigned int id)
 Callback for ast_unreal_pvt_callbacks optimization_started_cb.
static void local_pvt_destructor (void *vdoomed)
static struct ast_channellocal_request (const char *type, struct ast_format_cap *cap, const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *data, int *cause)
 Part of PBX interface.
static void local_shutdown (void)
static int locals_cmp_cb (void *obj, void *arg, int flags)
static char * locals_show (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 CLI command "local show channels".
static int manager_optimize_away (struct mansession *s, const struct message *m)
static void publish_local_bridge_message (struct local_pvt *p)
 STASIS_MESSAGE_TYPE_DEFN (ast_local_optimization_end_type,.to_ami=local_message_to_ami,)
 STASIS_MESSAGE_TYPE_DEFN (ast_local_optimization_begin_type,.to_ami=local_message_to_ami,)
 STASIS_MESSAGE_TYPE_DEFN (ast_local_bridge_type,.to_ami=local_message_to_ami,)
 Define local channel message types.

Variables

static struct ast_cli_entry cli_local []
static struct ast_channel_tech local_tech
struct ast_unreal_pvt_callbacks local_unreal_callbacks
 Callbacks from the unreal core when channel optimization occurs.
static struct ao2_containerlocals
static const char tdesc [] = "Local Proxy Channel Driver"


Detailed Description

Local proxy channel driver.

Author:
Richard Mudgett <rmudgett@digium.com>
See Also:

Definition in file core_local.c.


Enumeration Type Documentation

What to do with the ;2 channel when ast_call() happens.

Enumerator:
LOCAL_CALL_ACTION_DIALPLAN 
LOCAL_CALL_ACTION_BRIDGE 
LOCAL_CALL_ACTION_MASQUERADE 

Definition at line 194 of file core_local.c.

00194                        {
00195    /* The ast_call() will run dialplan on the ;2 channel. */
00196    LOCAL_CALL_ACTION_DIALPLAN,
00197    /* The ast_call() will impart the ;2 channel into a bridge. */
00198    LOCAL_CALL_ACTION_BRIDGE,
00199    /* The ast_call() will masquerade the ;2 channel into a channel. */
00200    LOCAL_CALL_ACTION_MASQUERADE,
00201 };


Function Documentation

struct ast_channel* ast_local_get_peer ( struct ast_channel ast  )  [read]

Get the other local channel in the pair.

Since:
12.0.0
Parameters:
ast Local channel to get peer.
Note:
On entry, ast must be locked.
Return values:
peer reffed on success.
NULL if no peer or error.

Definition at line 238 of file core_local.c.

References ao2_find, ao2_lock, ao2_ref, ao2_unlock, ast_channel_ref, ast_channel_tech_pvt(), local_pvt::base, ast_unreal_pvt::chan, NULL, and ast_unreal_pvt::owner.

Referenced by ari_channels_handle_originate_with_id(), attended_transfer_bridge(), and park_local_transfer().

00239 {
00240    struct local_pvt *p = ast_channel_tech_pvt(ast);
00241    struct local_pvt *found;
00242    struct ast_channel *peer;
00243 
00244    if (!p) {
00245       return NULL;
00246    }
00247 
00248    found = p ? ao2_find(locals, p, 0) : NULL;
00249    if (!found) {
00250       /* ast is either not a local channel or it has alredy been hungup */
00251       return NULL;
00252    }
00253    ao2_lock(found);
00254    if (ast == p->base.owner) {
00255       peer = p->base.chan;
00256    } else if (ast == p->base.chan) {
00257       peer = p->base.owner;
00258    } else {
00259       peer = NULL;
00260    }
00261    if (peer) {
00262       ast_channel_ref(peer);
00263    }
00264    ao2_unlock(found);
00265    ao2_ref(found, -1);
00266    return peer;
00267 }

int ast_local_init ( void   ) 

Initialize the local proxy channel.

Since:
12.0.0
Return values:
0 on success.
-1 on error.

Definition at line 1017 of file core_local.c.

References AO2_ALLOC_OPT_LOCK_MUTEX, ao2_cleanup, ao2_container_alloc_list, ao2_ref, ARRAY_LEN, ast_channel_register(), ast_cli_register_multiple(), ast_format_cap_alloc, ast_format_cap_append_by_type(), AST_FORMAT_CAP_FLAG_DEFAULT, ast_local_bridge_type(), ast_local_optimization_begin_type(), ast_local_optimization_end_type(), ast_log, ast_manager_register_xml_core, AST_MEDIA_TYPE_UNKNOWN, ast_register_cleanup(), ast_channel_tech::capabilities, EVENT_FLAG_CALL, EVENT_FLAG_SYSTEM, local_shutdown(), locals_cmp_cb(), LOG_ERROR, manager_optimize_away(), NULL, and STASIS_MESSAGE_TYPE_INIT.

Referenced by main().

01018 {
01019 
01020    if (STASIS_MESSAGE_TYPE_INIT(ast_local_optimization_begin_type)) {
01021       return -1;
01022    }
01023 
01024    if (STASIS_MESSAGE_TYPE_INIT(ast_local_optimization_end_type)) {
01025       return -1;
01026    }
01027 
01028    if (STASIS_MESSAGE_TYPE_INIT(ast_local_bridge_type)) {
01029       return -1;
01030    }
01031 
01032    if (!(local_tech.capabilities = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT))) {
01033       return -1;
01034    }
01035    ast_format_cap_append_by_type(local_tech.capabilities, AST_MEDIA_TYPE_UNKNOWN);
01036 
01037    locals = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_MUTEX, 0, NULL, locals_cmp_cb);
01038    if (!locals) {
01039       ao2_cleanup(local_tech.capabilities);
01040       local_tech.capabilities = NULL;
01041       return -1;
01042    }
01043 
01044    /* Make sure we can register our channel type */
01045    if (ast_channel_register(&local_tech)) {
01046       ast_log(LOG_ERROR, "Unable to register channel class 'Local'\n");
01047       ao2_ref(locals, -1);
01048       ao2_cleanup(local_tech.capabilities);
01049       local_tech.capabilities = NULL;
01050       return -1;
01051    }
01052    ast_cli_register_multiple(cli_local, ARRAY_LEN(cli_local));
01053    ast_manager_register_xml_core("LocalOptimizeAway", EVENT_FLAG_SYSTEM|EVENT_FLAG_CALL, manager_optimize_away);
01054 
01055    ast_register_cleanup(local_shutdown);
01056    return 0;
01057 }

int ast_local_setup_bridge ( struct ast_channel ast,
struct ast_bridge bridge,
struct ast_channel swap,
struct ast_bridge_features features 
)

Setup the outgoing local channel to join a bridge on ast_call().

Since:
12.0.0
Parameters:
ast Either channel of a local channel pair.
bridge Bridge to join.
swap Channel to swap with when joining.
features Bridge features structure.
Note:
The features parameter must be NULL or obtained by ast_bridge_features_new(). You must not dereference features after calling even if the call fails.

Intended to be called after ast_request() and before ast_call() on a local channel.

Return values:
0 on success.
-1 on error.

Definition at line 561 of file core_local.c.

References local_pvt::action, ao2_find, ao2_lock, ao2_ref, ao2_unlock, ast_bridge_features_destroy(), ast_channel_lock, ast_channel_ref, ast_channel_tech_pvt(), ast_channel_unlock, ast_test_flag, AST_UNREAL_CARETAKER_THREAD, local_pvt::base, local_pvt::bridge, ast_unreal_pvt::chan, local_bridge::features, local_bridge::join, LOCAL_CALL_ACTION_BRIDGE, LOCAL_CALL_ACTION_DIALPLAN, NULL, ast_unreal_pvt::owner, local_bridge::swap, and local_pvt::type.

Referenced by attended_transfer_bridge().

00562 {
00563    struct local_pvt *p;
00564    struct local_pvt *found;
00565    int res = -1;
00566 
00567    /* Sanity checks. */
00568    if (!ast || !bridge) {
00569       ast_bridge_features_destroy(features);
00570       return -1;
00571    }
00572 
00573    ast_channel_lock(ast);
00574    p = ast_channel_tech_pvt(ast);
00575    ast_channel_unlock(ast);
00576 
00577    found = p ? ao2_find(locals, p, 0) : NULL;
00578    if (found) {
00579       ao2_lock(found);
00580       if (found->type == LOCAL_CALL_ACTION_DIALPLAN
00581          && found->base.owner
00582          && found->base.chan
00583          && !ast_test_flag(&found->base, AST_UNREAL_CARETAKER_THREAD)) {
00584          ao2_ref(bridge, +1);
00585          if (swap) {
00586             ast_channel_ref(swap);
00587          }
00588          found->type = LOCAL_CALL_ACTION_BRIDGE;
00589          found->action.bridge.join = bridge;
00590          found->action.bridge.swap = swap;
00591          found->action.bridge.features = features;
00592          res = 0;
00593       } else {
00594          ast_bridge_features_destroy(features);
00595       }
00596       ao2_unlock(found);
00597       ao2_ref(found, -1);
00598    }
00599 
00600    return res;
00601 }

int ast_local_setup_masquerade ( struct ast_channel ast,
struct ast_channel masq 
)

Setup the outgoing local channel to masquerade into a channel on ast_call().

Since:
12.0.0
Parameters:
ast Either channel of a local channel pair.
masq Channel to masquerade into.
Note:
Intended to be called after ast_request() and before ast_call() on a local channel.
Return values:
0 on success.
-1 on error.

Definition at line 603 of file core_local.c.

References local_pvt::action, ao2_find, ao2_lock, ao2_ref, ao2_unlock, ast_channel_lock, ast_channel_ref, ast_channel_tech_pvt(), ast_channel_unlock, ast_test_flag, AST_UNREAL_CARETAKER_THREAD, local_pvt::base, ast_unreal_pvt::chan, LOCAL_CALL_ACTION_DIALPLAN, LOCAL_CALL_ACTION_MASQUERADE, local_pvt::masq, NULL, ast_unreal_pvt::owner, and local_pvt::type.

Referenced by attended_transfer_bridge().

00604 {
00605    struct local_pvt *p;
00606    struct local_pvt *found;
00607    int res = -1;
00608 
00609    /* Sanity checks. */
00610    if (!ast || !masq) {
00611       return -1;
00612    }
00613 
00614    ast_channel_lock(ast);
00615    p = ast_channel_tech_pvt(ast);
00616    ast_channel_unlock(ast);
00617 
00618    found = p ? ao2_find(locals, p, 0) : NULL;
00619    if (found) {
00620       ao2_lock(found);
00621       if (found->type == LOCAL_CALL_ACTION_DIALPLAN
00622          && found->base.owner
00623          && found->base.chan
00624          && !ast_test_flag(&found->base, AST_UNREAL_CARETAKER_THREAD)) {
00625          ast_channel_ref(masq);
00626          found->type = LOCAL_CALL_ACTION_MASQUERADE;
00627          found->action.masq = masq;
00628          res = 0;
00629       }
00630       ao2_unlock(found);
00631       ao2_ref(found, -1);
00632    }
00633 
00634    return res;
00635 }

static struct local_pvt* local_alloc ( const char *  data,
struct ast_format_cap cap 
) [static, read]

Create a call structure.

Definition at line 828 of file core_local.c.

References ast_clear_flag, ast_copy_string(), AST_JB_ENABLED, ast_log, ast_set_flag, ast_strdupa, ast_test_flag, ast_unreal_alloc(), AST_UNREAL_MOH_INTERCEPT, AST_UNREAL_NO_OPTIMIZATION, local_pvt::base, ast_unreal_pvt::callbacks, local_pvt::context, context, local_pvt::exten, ast_unreal_pvt::jb_conf, local_pvt_destructor(), LOG_ERROR, ast_unreal_pvt::name, NULL, parse(), and S_OR.

Referenced by local_request().

00829 {
00830    struct local_pvt *pvt;
00831    char *parse;
00832    char *context;
00833    char *opts;
00834 
00835    pvt = (struct local_pvt *) ast_unreal_alloc(sizeof(*pvt), local_pvt_destructor, cap);
00836    if (!pvt) {
00837       return NULL;
00838    }
00839    pvt->base.callbacks = &local_unreal_callbacks;
00840 
00841    parse = ast_strdupa(data);
00842 
00843    /*
00844     * Local channels intercept MOH by default.
00845     *
00846     * This is a silly default because it represents state held by
00847     * the local channels.  Unless local channel optimization is
00848     * disabled, the state will dissapear when the local channels
00849     * optimize out.
00850     */
00851    ast_set_flag(&pvt->base, AST_UNREAL_MOH_INTERCEPT);
00852 
00853    /* Look for options */
00854    if ((opts = strchr(parse, '/'))) {
00855       *opts++ = '\0';
00856       if (strchr(opts, 'n')) {
00857          ast_set_flag(&pvt->base, AST_UNREAL_NO_OPTIMIZATION);
00858       }
00859       if (strchr(opts, 'j')) {
00860          if (ast_test_flag(&pvt->base, AST_UNREAL_NO_OPTIMIZATION)) {
00861             ast_set_flag(&pvt->base.jb_conf, AST_JB_ENABLED);
00862          } else {
00863             ast_log(LOG_ERROR, "You must use the 'n' option with the 'j' option to enable the jitter buffer\n");
00864          }
00865       }
00866       if (strchr(opts, 'm')) {
00867          ast_clear_flag(&pvt->base, AST_UNREAL_MOH_INTERCEPT);
00868       }
00869    }
00870 
00871    /* Look for a context */
00872    if ((context = strchr(parse, '@'))) {
00873       *context++ = '\0';
00874    }
00875 
00876    ast_copy_string(pvt->context, S_OR(context, "default"), sizeof(pvt->context));
00877    ast_copy_string(pvt->exten, parse, sizeof(pvt->exten));
00878    snprintf(pvt->base.name, sizeof(pvt->base.name), "%s@%s", pvt->exten, pvt->context);
00879 
00880    return pvt; /* this is returned with a ref */
00881 }

static int local_call ( struct ast_channel ast,
const char *  dest,
int  timeout 
) [static]

Initiate new call, part of PBX interface dest is the dial string.

Definition at line 639 of file core_local.c.

References local_pvt::action, ao2_cleanup, ao2_lock, ao2_ref, ao2_unlock, ast_answer(), ast_bridge_impart(), AST_BRIDGE_IMPART_CHAN_INDEPENDENT, ast_channel_caller(), ast_channel_lock, ast_channel_move(), ast_channel_tech_pvt(), ast_channel_unlock, ast_channel_unref, ast_exists_extension(), ast_hangup(), ast_log, ast_pbx_start(), ast_set_cc_interfaces_chanvar(), ast_set_flag, ast_strdupa, ast_unreal_call_setup(), AST_UNREAL_CARETAKER_THREAD, ast_unreal_lock_all(), local_pvt::base, local_pvt::bridge, local_pvt::context, local_pvt::exten, local_bridge::features, local_bridge::join, LOCAL_CALL_ACTION_BRIDGE, LOCAL_CALL_ACTION_DIALPLAN, LOCAL_CALL_ACTION_MASQUERADE, LOG_NOTICE, local_pvt::masq, NULL, publish_local_bridge_message(), S_COR, local_bridge::swap, and local_pvt::type.

00640 {
00641    struct local_pvt *p = ast_channel_tech_pvt(ast);
00642    int pvt_locked = 0;
00643 
00644    struct ast_channel *owner = NULL;
00645    struct ast_channel *chan = NULL;
00646    int res;
00647    char *reduced_dest = ast_strdupa(dest);
00648    char *slash;
00649    const char *chan_cid;
00650 
00651    if (!p) {
00652       return -1;
00653    }
00654 
00655    /* since we are letting go of channel locks that were locked coming into
00656     * this function, then we need to give the tech pvt a ref */
00657    ao2_ref(p, 1);
00658    ast_channel_unlock(ast);
00659 
00660    ast_unreal_lock_all(&p->base, &chan, &owner);
00661    pvt_locked = 1;
00662 
00663    if (owner != ast) {
00664       res = -1;
00665       goto return_cleanup;
00666    }
00667 
00668    if (!owner || !chan) {
00669       res = -1;
00670       goto return_cleanup;
00671    }
00672 
00673    ast_unreal_call_setup(owner, chan);
00674 
00675    /*
00676     * If the local channel has /n on the end of it, we need to lop
00677     * that off for our argument to setting up the CC_INTERFACES
00678     * variable.
00679     */
00680    if ((slash = strrchr(reduced_dest, '/'))) {
00681       *slash = '\0';
00682    }
00683    ast_set_cc_interfaces_chanvar(chan, reduced_dest);
00684 
00685    ao2_unlock(p);
00686    pvt_locked = 0;
00687 
00688    ast_channel_unlock(owner);
00689 
00690    chan_cid = S_COR(ast_channel_caller(chan)->id.number.valid,
00691       ast_channel_caller(chan)->id.number.str, NULL);
00692    if (chan_cid) {
00693       chan_cid = ast_strdupa(chan_cid);
00694    }
00695    ast_channel_unlock(chan);
00696 
00697    res = -1;
00698    switch (p->type) {
00699    case LOCAL_CALL_ACTION_DIALPLAN:
00700       if (!ast_exists_extension(NULL, p->context, p->exten, 1, chan_cid)) {
00701          ast_log(LOG_NOTICE, "No such extension/context %s@%s while calling Local channel\n",
00702             p->exten, p->context);
00703       } else {
00704          publish_local_bridge_message(p);
00705 
00706          /* Start switch on sub channel */
00707          res = ast_pbx_start(chan);
00708       }
00709       break;
00710    case LOCAL_CALL_ACTION_BRIDGE:
00711       publish_local_bridge_message(p);
00712       ast_answer(chan);
00713       res = ast_bridge_impart(p->action.bridge.join, chan, p->action.bridge.swap,
00714          p->action.bridge.features, AST_BRIDGE_IMPART_CHAN_INDEPENDENT);
00715       ao2_ref(p->action.bridge.join, -1);
00716       p->action.bridge.join = NULL;
00717       ao2_cleanup(p->action.bridge.swap);
00718       p->action.bridge.swap = NULL;
00719       p->action.bridge.features = NULL;
00720       break;
00721    case LOCAL_CALL_ACTION_MASQUERADE:
00722       publish_local_bridge_message(p);
00723       ast_answer(chan);
00724       res = ast_channel_move(p->action.masq, chan);
00725       if (!res) {
00726          /* Chan is now an orphaned zombie.  Destroy it. */
00727          ast_hangup(chan);
00728       }
00729       p->action.masq = ast_channel_unref(p->action.masq);
00730       break;
00731    }
00732    if (!res) {
00733       ao2_lock(p);
00734       ast_set_flag(&p->base, AST_UNREAL_CARETAKER_THREAD);
00735       ao2_unlock(p);
00736    }
00737 
00738    /* we already unlocked them, clear them here so the cleanup label won't touch them. */
00739    owner = ast_channel_unref(owner);
00740    chan = ast_channel_unref(chan);
00741 
00742 return_cleanup:
00743    if (p) {
00744       if (pvt_locked) {
00745          ao2_unlock(p);
00746       }
00747       ao2_ref(p, -1);
00748    }
00749    if (chan) {
00750       ast_channel_unlock(chan);
00751       ast_channel_unref(chan);
00752    }
00753 
00754    /*
00755     * owner is supposed to be == to ast, if it is, don't unlock it
00756     * because ast must exit locked
00757     */
00758    if (owner) {
00759       if (owner != ast) {
00760          ast_channel_unlock(owner);
00761          ast_channel_lock(ast);
00762       }
00763       ast_channel_unref(owner);
00764    } else {
00765       /* we have to exit with ast locked */
00766       ast_channel_lock(ast);
00767    }
00768 
00769    return res;
00770 }

static struct ast_multi_channel_blob* local_channel_optimization_blob ( struct local_pvt p,
struct ast_json json_object 
) [static, read]

Definition at line 324 of file core_local.c.

References ao2_cleanup, ast_channel_snapshot_get_latest(), ast_channel_uniqueid(), ast_multi_channel_blob_add_channel(), ast_multi_channel_blob_create(), local_pvt::base, ast_unreal_pvt::chan, NULL, ast_unreal_pvt::owner, and RAII_VAR.

Referenced by local_optimization_finished_cb(), and local_optimization_started_cb().

00326 {
00327    struct ast_multi_channel_blob *payload;
00328    RAII_VAR(struct ast_channel_snapshot *, local_one_snapshot, NULL, ao2_cleanup);
00329    RAII_VAR(struct ast_channel_snapshot *, local_two_snapshot, NULL, ao2_cleanup);
00330 
00331    local_one_snapshot = ast_channel_snapshot_get_latest(ast_channel_uniqueid(p->base.owner));
00332    if (!local_one_snapshot) {
00333       return NULL;
00334    }
00335 
00336    local_two_snapshot = ast_channel_snapshot_get_latest(ast_channel_uniqueid(p->base.chan));
00337    if (!local_two_snapshot) {
00338       return NULL;
00339    }
00340 
00341    payload = ast_multi_channel_blob_create(json_object);
00342    if (!payload) {
00343       return NULL;
00344    }
00345    ast_multi_channel_blob_add_channel(payload, "1", local_one_snapshot);
00346    ast_multi_channel_blob_add_channel(payload, "2", local_two_snapshot);
00347 
00348    return payload;
00349 }

static int local_devicestate ( const char *  data  )  [static]

Adds devicestate to local channels.

Definition at line 270 of file core_local.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_lock, ao2_ref, ao2_unlock, ast_debug, AST_DEVICE_INUSE, AST_DEVICE_INVALID, AST_DEVICE_NOT_INUSE, ast_exists_extension(), ast_log, ast_strdupa, ast_test_flag, AST_UNREAL_CARETAKER_THREAD, local_pvt::base, local_pvt::context, context, local_pvt::exten, exten, LOG_WARNING, NULL, and ast_unreal_pvt::owner.

00271 {
00272    int is_inuse = 0;
00273    int res = AST_DEVICE_INVALID;
00274    char *exten = ast_strdupa(data);
00275    char *context;
00276    char *opts;
00277    struct local_pvt *lp;
00278    struct ao2_iterator it;
00279 
00280    /* Strip options if they exist */
00281    opts = strchr(exten, '/');
00282    if (opts) {
00283       *opts = '\0';
00284    }
00285 
00286    context = strchr(exten, '@');
00287    if (!context) {
00288       ast_log(LOG_WARNING,
00289          "Someone used Local/%s somewhere without a @context. This is bad.\n", data);
00290       return AST_DEVICE_INVALID;
00291    }
00292    *context++ = '\0';
00293 
00294    it = ao2_iterator_init(locals, 0);
00295    for (; (lp = ao2_iterator_next(&it)); ao2_ref(lp, -1)) {
00296       ao2_lock(lp);
00297       if (!strcmp(exten, lp->exten)
00298          && !strcmp(context, lp->context)) {
00299          res = AST_DEVICE_NOT_INUSE;
00300          if (lp->base.owner
00301             && ast_test_flag(&lp->base, AST_UNREAL_CARETAKER_THREAD)) {
00302             is_inuse = 1;
00303          }
00304       }
00305       ao2_unlock(lp);
00306       if (is_inuse) {
00307          res = AST_DEVICE_INUSE;
00308          ao2_ref(lp, -1);
00309          break;
00310       }
00311    }
00312    ao2_iterator_destroy(&it);
00313 
00314    if (res == AST_DEVICE_INVALID) {
00315       ast_debug(3, "Checking if extension %s@%s exists (devicestate)\n", exten, context);
00316       if (ast_exists_extension(NULL, context, exten, 1, NULL)) {
00317          res = AST_DEVICE_NOT_INUSE;
00318       }
00319    }
00320 
00321    return res;
00322 }

static int local_hangup ( struct ast_channel ast  )  [static]

Hangup a call through the local proxy channel.

Definition at line 773 of file core_local.c.

References ao2_lock, ao2_ref, ao2_unlink, ao2_unlock, ast_channel_tech_pvt(), ast_unreal_hangup(), local_pvt::base, ast_unreal_pvt::chan, and ast_unreal_pvt::owner.

00774 {
00775    struct local_pvt *p = ast_channel_tech_pvt(ast);
00776    int res;
00777 
00778    if (!p) {
00779       return -1;
00780    }
00781 
00782    /* give the pvt a ref to fulfill calling requirements. */
00783    ao2_ref(p, +1);
00784    res = ast_unreal_hangup(&p->base, ast);
00785    if (!res) {
00786       int unlink;
00787 
00788       ao2_lock(p);
00789       unlink = !p->base.owner && !p->base.chan;
00790       ao2_unlock(p);
00791       if (unlink) {
00792          ao2_unlink(locals, p);
00793       }
00794    }
00795    ao2_ref(p, -1);
00796 
00797    return res;
00798 }

static struct ast_manager_event_blob * local_message_to_ami ( struct stasis_message msg  )  [static, read]

Definition at line 425 of file core_local.c.

References ast_free, ast_json_integer_get(), ast_json_is_true(), ast_json_object_get(), ast_json_string_get(), ast_local_bridge_type(), ast_local_optimization_begin_type(), ast_local_optimization_end_type(), ast_manager_build_channel_state_string_prefix(), ast_manager_event_blob_create(), ast_multi_channel_blob_get_channel(), ast_multi_channel_blob_get_json(), ast_str_append(), ast_str_buffer(), ast_str_create(), AST_UNREAL_OWNER, EVENT_FLAG_CALL, NULL, RAII_VAR, stasis_message_data(), stasis_message_type(), and ast_channel_snapshot::uniqueid.

00426 {
00427    struct ast_multi_channel_blob *obj = stasis_message_data(message);
00428    struct ast_json *blob = ast_multi_channel_blob_get_json(obj);
00429    struct ast_channel_snapshot *local_snapshot_one;
00430    struct ast_channel_snapshot *local_snapshot_two;
00431    RAII_VAR(struct ast_str *, local_channel_one, NULL, ast_free);
00432    RAII_VAR(struct ast_str *, local_channel_two, NULL, ast_free);
00433    RAII_VAR(struct ast_str *, event_buffer, NULL, ast_free);
00434    const char *event;
00435 
00436    local_snapshot_one = ast_multi_channel_blob_get_channel(obj, "1");
00437    local_snapshot_two = ast_multi_channel_blob_get_channel(obj, "2");
00438    if (!local_snapshot_one || !local_snapshot_two) {
00439       return NULL;
00440    }
00441 
00442    event_buffer = ast_str_create(1024);
00443    local_channel_one = ast_manager_build_channel_state_string_prefix(local_snapshot_one, "LocalOne");
00444    local_channel_two = ast_manager_build_channel_state_string_prefix(local_snapshot_two, "LocalTwo");
00445    if (!event_buffer || !local_channel_one || !local_channel_two) {
00446       return NULL;
00447    }
00448 
00449    if (stasis_message_type(message) == ast_local_optimization_begin_type()) {
00450       struct ast_channel_snapshot *source_snapshot;
00451       RAII_VAR(struct ast_str *, source_str, NULL, ast_free);
00452       const char *dest_uniqueid;
00453 
00454       source_snapshot = ast_multi_channel_blob_get_channel(obj, "source");
00455       if (source_snapshot) {
00456          source_str = ast_manager_build_channel_state_string_prefix(source_snapshot, "Source");
00457          if (!source_str) {
00458             return NULL;
00459          }
00460       }
00461 
00462       dest_uniqueid = ast_json_object_get(blob, "dest") == AST_UNREAL_OWNER ?
00463             local_snapshot_one->uniqueid : local_snapshot_two->uniqueid;
00464 
00465       event = "LocalOptimizationBegin";
00466       if (source_str) {
00467          ast_str_append(&event_buffer, 0, "%s", ast_str_buffer(source_str));
00468       }
00469       ast_str_append(&event_buffer, 0, "DestUniqueId: %s\r\n", dest_uniqueid);
00470       ast_str_append(&event_buffer, 0, "Id: %u\r\n", (unsigned int) ast_json_integer_get(ast_json_object_get(blob, "id")));
00471    } else if (stasis_message_type(message) == ast_local_optimization_end_type()) {
00472       event = "LocalOptimizationEnd";
00473       ast_str_append(&event_buffer, 0, "Success: %s\r\n", ast_json_integer_get(ast_json_object_get(blob, "success")) ? "Yes" : "No");
00474       ast_str_append(&event_buffer, 0, "Id: %u\r\n", (unsigned int) ast_json_integer_get(ast_json_object_get(blob, "id")));
00475    } else if (stasis_message_type(message) == ast_local_bridge_type()) {
00476       event = "LocalBridge";
00477       ast_str_append(&event_buffer, 0, "Context: %s\r\n", ast_json_string_get(ast_json_object_get(blob, "context")));
00478       ast_str_append(&event_buffer, 0, "Exten: %s\r\n", ast_json_string_get(ast_json_object_get(blob, "exten")));
00479       ast_str_append(&event_buffer, 0, "LocalOptimization: %s\r\n", ast_json_is_true(ast_json_object_get(blob, "can_optimize")) ? "Yes" : "No");
00480    } else {
00481       return NULL;
00482    }
00483 
00484    return ast_manager_event_blob_create(EVENT_FLAG_CALL, event,
00485       "%s"
00486       "%s"
00487       "%s",
00488       ast_str_buffer(local_channel_one),
00489       ast_str_buffer(local_channel_two),
00490       ast_str_buffer(event_buffer));
00491 }

static void local_optimization_finished_cb ( struct ast_unreal_pvt base,
int  success,
unsigned int  id 
) [static]

Callback for ast_unreal_pvt_callbacks optimization_finished_cb.

Definition at line 395 of file core_local.c.

References ao2_cleanup, ast_channel_topic(), ast_json_null(), ast_json_pack(), ast_json_unref(), ast_local_optimization_end_type(), local_pvt::base, local_channel_optimization_blob(), NULL, ast_unreal_pvt::owner, RAII_VAR, stasis_message_create(), and stasis_publish().

00396 {
00397    RAII_VAR(struct ast_json *, json_object, ast_json_null(), ast_json_unref);
00398    RAII_VAR(struct ast_multi_channel_blob *, payload, NULL, ao2_cleanup);
00399    RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
00400    struct local_pvt *p = (struct local_pvt *)base;
00401 
00402    if (!ast_local_optimization_end_type()) {
00403       return;
00404    }
00405 
00406    json_object = ast_json_pack("{s: i, s: i}", "success", success, "id", id);
00407 
00408    if (!json_object) {
00409       return;
00410    }
00411 
00412    payload = local_channel_optimization_blob(p, json_object);
00413    if (!payload) {
00414       return;
00415    }
00416 
00417    msg = stasis_message_create(ast_local_optimization_end_type(), payload);
00418    if (!msg) {
00419       return;
00420    }
00421 
00422    stasis_publish(ast_channel_topic(p->base.owner), msg);
00423 }

static void local_optimization_started_cb ( struct ast_unreal_pvt base,
struct ast_channel source,
enum ast_unreal_channel_indicator  dest,
unsigned int  id 
) [static]

Callback for ast_unreal_pvt_callbacks optimization_started_cb.

Definition at line 352 of file core_local.c.

References ao2_cleanup, ast_channel_snapshot_get_latest(), ast_channel_topic(), ast_channel_uniqueid(), ast_json_null(), ast_json_pack(), ast_json_unref(), ast_local_optimization_begin_type(), ast_multi_channel_blob_add_channel(), local_pvt::base, local_channel_optimization_blob(), NULL, ast_unreal_pvt::owner, RAII_VAR, stasis_message_create(), and stasis_publish().

00354 {
00355    RAII_VAR(struct ast_json *, json_object, ast_json_null(), ast_json_unref);
00356    RAII_VAR(struct ast_multi_channel_blob *, payload, NULL, ao2_cleanup);
00357    RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
00358    struct local_pvt *p = (struct local_pvt *)base;
00359 
00360    if (!ast_local_optimization_begin_type()) {
00361       return;
00362    }
00363 
00364    json_object = ast_json_pack("{s: i, s: i}",
00365          "dest", dest, "id", id);
00366 
00367    if (!json_object) {
00368       return;
00369    }
00370 
00371    payload = local_channel_optimization_blob(p, json_object);
00372    if (!payload) {
00373       return;
00374    }
00375 
00376    if (source) {
00377       RAII_VAR(struct ast_channel_snapshot *, source_snapshot, NULL, ao2_cleanup);
00378       source_snapshot = ast_channel_snapshot_get_latest(ast_channel_uniqueid(source));
00379       if (!source_snapshot) {
00380          return;
00381       }
00382 
00383       ast_multi_channel_blob_add_channel(payload, "source", source_snapshot);
00384    }
00385 
00386    msg = stasis_message_create(ast_local_optimization_begin_type(), payload);
00387    if (!msg) {
00388       return;
00389    }
00390 
00391    stasis_publish(ast_channel_topic(p->base.owner), msg);
00392 }

static void local_pvt_destructor ( void *  vdoomed  )  [static]

static struct ast_channel * local_request ( const char *  type,
struct ast_format_cap cap,
const struct ast_assigned_ids assignedids,
const struct ast_channel requestor,
const char *  data,
int *  cause 
) [static, read]

Part of PBX interface.

Definition at line 884 of file core_local.c.

References ao2_link, ao2_ref, ast_read_threadstorage_callid(), AST_STATE_DOWN, AST_STATE_RING, ast_unreal_new_channels(), local_pvt::base, ast_channel::callid, local_pvt::context, local_pvt::exten, local_alloc(), and NULL.

00885 {
00886    struct local_pvt *p;
00887    struct ast_channel *chan;
00888    ast_callid callid;
00889 
00890    /* Allocate a new private structure and then Asterisk channels */
00891    p = local_alloc(data, cap);
00892    if (!p) {
00893       return NULL;
00894    }
00895    callid = ast_read_threadstorage_callid();
00896    chan = ast_unreal_new_channels(&p->base, &local_tech, AST_STATE_DOWN, AST_STATE_RING,
00897       p->exten, p->context, assignedids, requestor, callid);
00898    if (chan) {
00899       ao2_link(locals, p);
00900    }
00901    ao2_ref(p, -1); /* kill the ref from the alloc */
00902 
00903    return chan;
00904 }

static void local_shutdown ( void   )  [static]

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

Definition at line 987 of file core_local.c.

References CMP_MATCH.

Referenced by ast_local_init().

00988 {
00989    return (obj == arg) ? CMP_MATCH : 0;
00990 }

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

CLI command "local show channels".

Definition at line 907 of file core_local.c.

References ao2_container_count(), ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_lock, ao2_ref, ao2_unlock, ast_cli_args::argc, ast_channel_name(), ast_cli(), local_pvt::base, CLI_GENERATE, CLI_INIT, CLI_SHOWUSAGE, CLI_SUCCESS, ast_cli_entry::command, ast_cli_args::fd, ast_unreal_pvt::name, NULL, ast_unreal_pvt::owner, RESULT_SUCCESS, and ast_cli_entry::usage.

00908 {
00909    struct local_pvt *p;
00910    struct ao2_iterator it;
00911 
00912    switch (cmd) {
00913    case CLI_INIT:
00914       e->command = "local show channels";
00915       e->usage =
00916          "Usage: local show channels\n"
00917          "       Provides summary information on active local proxy channels.\n";
00918       return NULL;
00919    case CLI_GENERATE:
00920       return NULL;
00921    }
00922 
00923    if (a->argc != 3) {
00924       return CLI_SHOWUSAGE;
00925    }
00926 
00927    if (ao2_container_count(locals) == 0) {
00928       ast_cli(a->fd, "No local channels in use\n");
00929       return RESULT_SUCCESS;
00930    }
00931 
00932    it = ao2_iterator_init(locals, 0);
00933    while ((p = ao2_iterator_next(&it))) {
00934       ao2_lock(p);
00935       ast_cli(a->fd, "%s -- %s\n",
00936          p->base.owner ? ast_channel_name(p->base.owner) : "<unowned>",
00937          p->base.name);
00938       ao2_unlock(p);
00939       ao2_ref(p, -1);
00940    }
00941    ao2_iterator_destroy(&it);
00942 
00943    return CLI_SUCCESS;
00944 }

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

Definition at line 950 of file core_local.c.

References ao2_find, ao2_lock, ao2_ref, ao2_unlock, ast_channel_get_by_name(), ast_channel_tech_pvt(), ast_channel_unref, ast_clear_flag, ast_strlen_zero, AST_UNREAL_NO_OPTIMIZATION, astman_get_header(), astman_send_ack(), astman_send_error(), local_pvt::base, and NULL.

Referenced by ast_local_init().

00951 {
00952    const char *channel;
00953    struct local_pvt *p;
00954    struct local_pvt *found;
00955    struct ast_channel *chan;
00956 
00957    channel = astman_get_header(m, "Channel");
00958    if (ast_strlen_zero(channel)) {
00959       astman_send_error(s, m, "'Channel' not specified.");
00960       return 0;
00961    }
00962 
00963    chan = ast_channel_get_by_name(channel);
00964    if (!chan) {
00965       astman_send_error(s, m, "Channel does not exist.");
00966       return 0;
00967    }
00968 
00969    p = ast_channel_tech_pvt(chan);
00970    ast_channel_unref(chan);
00971 
00972    found = p ? ao2_find(locals, p, 0) : NULL;
00973    if (found) {
00974       ao2_lock(found);
00975       ast_clear_flag(&found->base, AST_UNREAL_NO_OPTIMIZATION);
00976       ao2_unlock(found);
00977       ao2_ref(found, -1);
00978       astman_send_ack(s, m, "Queued channel to be optimized away");
00979    } else {
00980       astman_send_error(s, m, "Unable to find channel");
00981    }
00982 
00983    return 0;
00984 }

static void publish_local_bridge_message ( struct local_pvt p  )  [static]

Definition at line 502 of file core_local.c.

References ao2_cleanup, ao2_unlock, ast_channel_snapshot_get_latest(), ast_channel_topic(), ast_channel_uniqueid(), ast_channel_unlock, ast_channel_unref, ast_json_pack(), ast_json_unref(), ast_local_bridge_type(), ast_multi_channel_blob_add_channel(), ast_multi_channel_blob_create(), ast_test_flag, ast_unreal_lock_all(), AST_UNREAL_NO_OPTIMIZATION, local_pvt::base, local_pvt::context, end, local_pvt::exten, NULL, RAII_VAR, stasis_message_create(), and stasis_publish().

Referenced by local_call().

00503 {
00504    RAII_VAR(struct ast_multi_channel_blob *, multi_blob, NULL, ao2_cleanup);
00505    RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
00506    RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
00507    RAII_VAR(struct ast_channel_snapshot *, one_snapshot, NULL, ao2_cleanup);
00508    RAII_VAR(struct ast_channel_snapshot *, two_snapshot, NULL, ao2_cleanup);
00509    struct ast_channel *owner;
00510    struct ast_channel *chan;
00511 
00512    if (!ast_local_bridge_type()) {
00513       return;
00514    }
00515 
00516    ast_unreal_lock_all(&p->base, &chan, &owner);
00517 
00518    blob = ast_json_pack("{s: s, s: s, s: b}",
00519       "context", p->context,
00520       "exten", p->exten,
00521       "can_optimize", !ast_test_flag(&p->base, AST_UNREAL_NO_OPTIMIZATION));
00522    if (!blob) {
00523       goto end;
00524    }
00525 
00526    multi_blob = ast_multi_channel_blob_create(blob);
00527    if (!multi_blob) {
00528       goto end;
00529    }
00530 
00531    one_snapshot = ast_channel_snapshot_get_latest(ast_channel_uniqueid(owner));
00532    if (!one_snapshot) {
00533       goto end;
00534    }
00535 
00536    two_snapshot = ast_channel_snapshot_get_latest(ast_channel_uniqueid(chan));
00537    if (!two_snapshot) {
00538       goto end;
00539    }
00540 
00541    ast_multi_channel_blob_add_channel(multi_blob, "1", one_snapshot);
00542    ast_multi_channel_blob_add_channel(multi_blob, "2", two_snapshot);
00543 
00544    msg = stasis_message_create(ast_local_bridge_type(), multi_blob);
00545    if (!msg) {
00546       goto end;
00547    }
00548 
00549    stasis_publish(ast_channel_topic(owner), msg);
00550 
00551 end:
00552    ast_channel_unlock(owner);
00553    ast_channel_unref(owner);
00554 
00555    ast_channel_unlock(chan);
00556    ast_channel_unref(chan);
00557 
00558    ao2_unlock(&p->base);
00559 }

STASIS_MESSAGE_TYPE_DEFN ( ast_local_optimization_end_type  ,
to_ami = local_message_to_ami 
)

STASIS_MESSAGE_TYPE_DEFN ( ast_local_optimization_begin_type  ,
to_ami = local_message_to_ami 
)

STASIS_MESSAGE_TYPE_DEFN ( ast_local_bridge_type  ,
to_ami = local_message_to_ami 
)

Define local channel message types.


Variable Documentation

struct ast_cli_entry cli_local[] [static]

Initial value:

 {
   AST_CLI_DEFINE(locals_show, "List status of local channels"),
}

Definition at line 946 of file core_local.c.

struct ast_channel_tech local_tech [static]

Definition at line 171 of file core_local.c.

Initial value:

 {
   .optimization_started = local_optimization_started_cb,
   .optimization_finished = local_optimization_finished_cb,
}
Callbacks from the unreal core when channel optimization occurs.

Definition at line 165 of file core_local.c.

struct ao2_container* locals [static]

Definition at line 138 of file core_local.c.

Referenced by attended_transfer_bridge().

const char tdesc[] = "Local Proxy Channel Driver" [static]

Definition at line 136 of file core_local.c.


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