Sat Aug 6 00:39:46 2011

Asterisk developer's documentation


chan_local.c File Reference

Local Proxy Channel. More...

#include "asterisk.h"
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <errno.h>
#include <stdlib.h>
#include <fcntl.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/signal.h>
#include "asterisk/lock.h"
#include "asterisk/channel.h"
#include "asterisk/config.h"
#include "asterisk/logger.h"
#include "asterisk/module.h"
#include "asterisk/pbx.h"
#include "asterisk/options.h"
#include "asterisk/sched.h"
#include "asterisk/io.h"
#include "asterisk/rtp.h"
#include "asterisk/acl.h"
#include "asterisk/callerid.h"
#include "asterisk/file.h"
#include "asterisk/cli.h"
#include "asterisk/app.h"
#include "asterisk/musiconhold.h"
#include "asterisk/manager.h"
#include "asterisk/stringfields.h"
#include "asterisk/devicestate.h"
#include "asterisk/astobj2.h"

Go to the source code of this file.

Data Structures

struct  local_pvt

Defines

#define IS_OUTBOUND(a, b)   (a == b->chan ? 1 : 0)
#define LOCAL_ALREADY_MASQED   (1 << 1)
#define LOCAL_LAUNCHED_PBX   (1 << 2)
#define LOCAL_MOH_PASSTHRU   (1 << 4)
#define LOCAL_NO_OPTIMIZATION   (1 << 3)

Functions

static void __reg_module (void)
static void __unreg_module (void)
static void check_bridge (struct local_pvt *p)
static int load_module (void)
 Load module into PBX, register channel.
static struct local_pvtlocal_alloc (const char *data, int format)
 Create a call structure.
static int local_answer (struct ast_channel *ast)
static int local_call (struct ast_channel *ast, char *dest, int timeout)
 Initiate new call, part of PBX interface dest is the dial string.
static int local_devicestate (void *data)
 Adds devicestate to local channels.
static int local_digit_begin (struct ast_channel *ast, char digit)
static int local_digit_end (struct ast_channel *ast, char digit, unsigned int duration)
static int local_fixup (struct ast_channel *oldchan, struct ast_channel *newchan)
static int local_hangup (struct ast_channel *ast)
 Hangup a call through the local proxy channel.
static int local_indicate (struct ast_channel *ast, int condition, const void *data, size_t datalen)
static struct ast_channellocal_new (struct local_pvt *p, int state)
 Start new local channel.
static int local_queue_frame (struct local_pvt *p, int isoutbound, struct ast_frame *f, struct ast_channel *us, int us_locked)
 queue a frame on a to either the p->owner or p->chan
static struct ast_framelocal_read (struct ast_channel *ast)
static struct ast_channellocal_request (const char *type, int format, void *data, int *cause)
 Part of PBX interface.
static int local_sendhtml (struct ast_channel *ast, int subclass, const char *data, int datalen)
static int local_sendtext (struct ast_channel *ast, const char *text)
static int local_setoption (struct ast_channel *chan, int option, void *data, int datalen)
static int local_write (struct ast_channel *ast, struct ast_frame *f)
static int locals_cmp_cb (void *obj, void *arg, int flags)
static int locals_show (int fd, int argc, char **argv)
 CLI command "local show channels".
static int unload_module (void)
 Unload the local proxy channel from Asterisk.

Variables

static struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT | AST_MODFLAG_BUILDSUM, .description = "Local Proxy Channel (Note: used internally by other modules)" , .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 = "361d7bb937402d51e4658efb5b4d76e4" , .load = load_module, .unload = unload_module, }
static const struct ast_module_infoast_module_info = &__mod_info
static const int BUCKET_SIZE = 1
static struct ast_cli_entry cli_local []
static struct ast_channel_tech local_tech
static struct ao2_containerlocals
static char show_locals_usage []
static const char tdesc [] = "Local Proxy Channel Driver"


Detailed Description

Local Proxy Channel.

Author:
Mark Spencer <markster@digium.com>

Definition in file chan_local.c.


Define Documentation

#define IS_OUTBOUND ( a,
 )     (a == b->chan ? 1 : 0)

Definition at line 68 of file chan_local.c.

#define LOCAL_ALREADY_MASQED   (1 << 1)

Already masqueraded

Definition at line 127 of file chan_local.c.

Referenced by check_bridge(), and local_write().

#define LOCAL_LAUNCHED_PBX   (1 << 2)

PBX was launched

Definition at line 128 of file chan_local.c.

Referenced by local_call(), and local_hangup().

#define LOCAL_MOH_PASSTHRU   (1 << 4)

Pass through music on hold start/stop frames

Definition at line 130 of file chan_local.c.

Referenced by local_alloc(), and local_indicate().

#define LOCAL_NO_OPTIMIZATION   (1 << 3)

Do not optimize using masquerading

Definition at line 129 of file chan_local.c.

Referenced by check_bridge(), and local_alloc().


Function Documentation

static void __reg_module ( void   )  [static]

Definition at line 946 of file chan_local.c.

00946 : used internally by other modules)");

static void __unreg_module ( void   )  [static]

Definition at line 946 of file chan_local.c.

00946 : used internally by other modules)");

static void check_bridge ( struct local_pvt p  )  [static]

Definition at line 312 of file chan_local.c.

References ast_channel::_bridge, ast_channel::_softhangup, ast_app_group_update(), ast_bridged_channel(), ast_channel_masquerade(), AST_LIST_EMPTY, ast_mutex_trylock(), ast_mutex_unlock(), ast_set_flag, ast_test_flag, ast_channel::audiohooks, local_pvt::chan, ast_channel::cid, ast_callerid::cid_ani, ast_callerid::cid_ani2, ast_callerid::cid_dnid, ast_callerid::cid_name, ast_callerid::cid_num, ast_callerid::cid_pres, ast_callerid::cid_rdnis, ast_callerid::cid_tns, ast_callerid::cid_ton, LOCAL_ALREADY_MASQED, LOCAL_NO_OPTIMIZATION, ast_channel::lock, ast_channel::monitor, local_pvt::owner, and ast_channel::readq.

Referenced by local_write().

00313 {
00314    struct ast_channel_monitor *tmp;
00315    if (ast_test_flag(p, LOCAL_ALREADY_MASQED) || ast_test_flag(p, LOCAL_NO_OPTIMIZATION) || !p->chan || !p->owner || (p->chan->_bridge != ast_bridged_channel(p->chan)))
00316       return;
00317 
00318    /* only do the masquerade if we are being called on the outbound channel,
00319       if it has been bridged to another channel and if there are no pending
00320       frames on the owner channel (because they would be transferred to the
00321       outbound channel during the masquerade)
00322    */
00323    if (p->chan->_bridge /* Not ast_bridged_channel!  Only go one step! */ && AST_LIST_EMPTY(&p->owner->readq)) {
00324       /* Masquerade bridged channel into owner */
00325       /* Lock everything we need, one by one, and give up if
00326          we can't get everything.  Remember, we'll get another
00327          chance in just a little bit */
00328       if (!ast_mutex_trylock(&(p->chan->_bridge)->lock)) {
00329          if (!p->chan->_bridge->_softhangup) {
00330             if (!ast_mutex_trylock(&p->owner->lock)) {
00331                if (!p->owner->_softhangup) {
00332                   if (p->owner->monitor && !p->chan->_bridge->monitor) {
00333                      /* If a local channel is being monitored, we don't want a masquerade
00334                       * to cause the monitor to go away. Since the masquerade swaps the monitors,
00335                       * pre-swapping the monitors before the masquerade will ensure that the monitor
00336                       * ends up where it is expected.
00337                       */
00338                      tmp = p->owner->monitor;
00339                      p->owner->monitor = p->chan->_bridge->monitor;
00340                      p->chan->_bridge->monitor = tmp;
00341                   }
00342                   if (p->chan->audiohooks) {
00343                      struct ast_audiohook_list *audiohooks_swapper;
00344                      audiohooks_swapper = p->chan->audiohooks;
00345                      p->chan->audiohooks = p->owner->audiohooks;
00346                      p->owner->audiohooks = audiohooks_swapper;
00347                   }
00348 
00349                   /* If any Caller ID was set, preserve it after masquerade like above. We must check
00350                    * to see if Caller ID was set because otherwise we'll mistakingly copy info not
00351                    * set from the dialplan and will overwrite the real channel Caller ID. The reason
00352                    * for this whole preswapping action is because the Caller ID is set on the channel
00353                    * thread (which is the to be masqueraded away local channel) before both local
00354                    * channels are optimized away.
00355                    */
00356                   if (p->owner->cid.cid_dnid || p->owner->cid.cid_num ||
00357                      p->owner->cid.cid_name || p->owner->cid.cid_ani ||
00358                      p->owner->cid.cid_rdnis || p->owner->cid.cid_pres ||  
00359                      p->owner->cid.cid_ani2 || p->owner->cid.cid_ton ||  
00360                      p->owner->cid.cid_tns) {
00361 
00362                      struct ast_callerid tmpcid;
00363                      tmpcid = p->owner->cid;
00364                      p->owner->cid = p->chan->_bridge->cid;
00365                      p->chan->_bridge->cid = tmpcid;
00366                   }
00367 
00368                   ast_app_group_update(p->chan, p->owner);
00369                   ast_channel_masquerade(p->owner, p->chan->_bridge);
00370                   ast_set_flag(p, LOCAL_ALREADY_MASQED);
00371                }
00372                ast_mutex_unlock(&p->owner->lock);
00373             }
00374          }
00375          ast_mutex_unlock(&(p->chan->_bridge)->lock);
00376       }
00377    }
00378 }

static int load_module ( void   )  [static]

Load module into PBX, register channel.

Definition at line 907 of file chan_local.c.

References ao2_container_alloc(), ao2_ref(), ast_channel_register(), ast_cli_register_multiple(), ast_log(), cli_local, local_tech, locals, locals_cmp_cb(), and LOG_ERROR.

00908 {
00909    if (!(locals = ao2_container_alloc(BUCKET_SIZE, NULL, locals_cmp_cb))) {
00910       return -1;
00911    }
00912 
00913    /* Make sure we can register our channel type */
00914    if (ast_channel_register(&local_tech)) {
00915       ast_log(LOG_ERROR, "Unable to register channel class 'Local'\n");
00916       ao2_ref(locals, -1);
00917       return -1;
00918    }
00919 
00920    ast_cli_register_multiple(cli_local, sizeof(cli_local) / sizeof(struct ast_cli_entry));
00921    return 0;
00922 }

static struct local_pvt* local_alloc ( const char *  data,
int  format 
) [static]

Create a call structure.

Definition at line 739 of file chan_local.c.

References ao2_alloc(), ast_copy_string(), ast_exists_extension(), ast_log(), ast_set_flag, LOCAL_MOH_PASSTHRU, LOCAL_NO_OPTIMIZATION, locals, and LOG_NOTICE.

Referenced by local_request().

00740 {
00741    struct local_pvt *tmp = NULL;
00742    char *c = NULL, *opts = NULL;
00743 
00744    if (!(tmp = ao2_alloc(sizeof(*tmp), NULL))) {
00745       return NULL;
00746    }
00747 
00748    /* Initialize private structure information */
00749    ast_copy_string(tmp->exten, data, sizeof(tmp->exten));
00750 
00751    /* Look for options */
00752    if ((opts = strchr(tmp->exten, '/'))) {
00753       *opts++ = '\0';
00754       if (strchr(opts, 'n'))
00755          ast_set_flag(tmp, LOCAL_NO_OPTIMIZATION);
00756       if (strchr(opts, 'm'))
00757          ast_set_flag(tmp, LOCAL_MOH_PASSTHRU);
00758    }
00759 
00760    /* Look for a context */
00761    if ((c = strchr(tmp->exten, '@')))
00762       *c++ = '\0';
00763 
00764    ast_copy_string(tmp->context, c ? c : "default", sizeof(tmp->context));
00765 
00766    tmp->reqformat = format;
00767 
00768 #if 0
00769    /* We can't do this check here, because we don't know the CallerID yet, and
00770     * the CallerID could potentially affect what step is actually taken (or
00771     * even if that step exists). */
00772    if (!ast_exists_extension(NULL, tmp->context, tmp->exten, 1, NULL)) {
00773       ast_log(LOG_NOTICE, "No such extension/context %s@%s creating local channel\n", tmp->exten, tmp->context);
00774       tmp = local_pvt_destroy(tmp);
00775    } else {
00776 #endif
00777       /* Add to list */
00778       ao2_link(locals, tmp);
00779 #if 0
00780    }
00781 #endif
00782    return tmp; /* this is returned with a ref */
00783 }

static int local_answer ( struct ast_channel ast  )  [static]

Definition at line 284 of file chan_local.c.

References answer, ao2_lock(), ao2_ref(), ao2_unlock(), AST_CONTROL_ANSWER, AST_FRAME_CONTROL, ast_log(), IS_OUTBOUND, local_queue_frame(), LOG_WARNING, and ast_channel::tech_pvt.

00285 {
00286    struct local_pvt *p = ast->tech_pvt;
00287    int isoutbound;
00288    int res = -1;
00289 
00290    if (!p)
00291       return -1;
00292 
00293    ao2_lock(p);
00294    ao2_ref(p, 1);
00295    isoutbound = IS_OUTBOUND(ast, p);
00296    if (isoutbound) {
00297       /* Pass along answer since somebody answered us */
00298       struct ast_frame answer = { AST_FRAME_CONTROL, AST_CONTROL_ANSWER };
00299       res = local_queue_frame(p, isoutbound, &answer, ast, 1);
00300    } else {
00301       ast_log(LOG_WARNING, "Huh?  Local is being asked to answer?\n");
00302    }
00303    ao2_unlock(p);
00304    ao2_ref(p, -1);
00305    return res;
00306 }

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

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

Definition at line 567 of file chan_local.c.

References ast_channel::accountcode, accountcode, ao2_lock(), ao2_unlock(), ast_calloc, ast_cdr_update(), ast_channel_datastore_inherit(), ast_exists_extension(), AST_LIST_INSERT_TAIL, AST_LIST_TRAVERSE, ast_log(), ast_pbx_start(), ast_set_flag, ast_strdup, ast_string_field_set, ast_channel::cdrflags, local_pvt::chan, ast_channel::cid, ast_callerid::cid_ani, ast_callerid::cid_ani2, ast_callerid::cid_dnid, ast_callerid::cid_name, ast_callerid::cid_num, ast_callerid::cid_pres, ast_callerid::cid_rdnis, ast_callerid::cid_tns, ast_callerid::cid_ton, ast_channel::context, ast_var_t::entries, ast_channel::exten, ast_channel::language, language, len(), LOCAL_LAUNCHED_PBX, LOG_NOTICE, ast_channel::musicclass, musicclass, ast_var_t::name, local_pvt::owner, ast_channel::tech_pvt, ast_var_t::value, and ast_channel::varshead.

00568 {
00569    struct local_pvt *p = ast->tech_pvt;
00570    int res = 0;
00571    struct ast_var_t *varptr = NULL, *new;
00572    size_t len, namelen;
00573 
00574    if (!p)
00575       return -1;
00576 
00577    ao2_lock(p);
00578 
00579    /*
00580     * Note that cid_num and cid_name aren't passed in the ast_channel_alloc
00581     * call, so it's done here instead.
00582     */
00583    p->chan->cid.cid_dnid = ast_strdup(p->owner->cid.cid_dnid);
00584    p->chan->cid.cid_num = ast_strdup(p->owner->cid.cid_num);
00585    p->chan->cid.cid_name = ast_strdup(p->owner->cid.cid_name);
00586    p->chan->cid.cid_rdnis = ast_strdup(p->owner->cid.cid_rdnis);
00587    p->chan->cid.cid_ani = ast_strdup(p->owner->cid.cid_ani);
00588    p->chan->cid.cid_pres = p->owner->cid.cid_pres;
00589    p->chan->cid.cid_ani2 = p->owner->cid.cid_ani2;
00590    p->chan->cid.cid_ton = p->owner->cid.cid_ton;
00591    p->chan->cid.cid_tns = p->owner->cid.cid_tns;
00592    ast_string_field_set(p->chan, language, p->owner->language);
00593    ast_string_field_set(p->chan, accountcode, p->owner->accountcode);
00594    ast_string_field_set(p->chan, musicclass, p->owner->musicclass);
00595    ast_cdr_update(p->chan);
00596    p->chan->cdrflags = p->owner->cdrflags;
00597 
00598    /* copy the channel variables from the incoming channel to the outgoing channel */
00599    /* Note that due to certain assumptions, they MUST be in the same order */
00600    AST_LIST_TRAVERSE(&p->owner->varshead, varptr, entries) {
00601       namelen = strlen(varptr->name);
00602       len = sizeof(struct ast_var_t) + namelen + strlen(varptr->value) + 2;
00603       if ((new = ast_calloc(1, len))) {
00604          memcpy(new, varptr, len);
00605          new->value = &(new->name[0]) + namelen + 1;
00606          AST_LIST_INSERT_TAIL(&p->chan->varshead, new, entries);
00607       }
00608    }
00609    ast_channel_datastore_inherit(p->owner, p->chan);
00610 
00611    if (!ast_exists_extension(p->chan, p->chan->context, p->chan->exten, 1, p->owner->cid.cid_num)) {
00612       ast_log(LOG_NOTICE, "No such extension/context %s@%s while calling Local channel\n", p->chan->exten, p->chan->context);
00613       ao2_unlock(p);
00614       return -1;
00615    }
00616 
00617    /* Start switch on sub channel */
00618    if (!(res = ast_pbx_start(p->chan)))
00619       ast_set_flag(p, LOCAL_LAUNCHED_PBX);
00620 
00621    ao2_unlock(p);
00622    return res;
00623 }

static int local_devicestate ( void *  data  )  [static]

Adds devicestate to local channels.

Definition at line 198 of file chan_local.c.

References AST_DEVICE_INVALID, AST_DEVICE_UNKNOWN, ast_exists_extension(), ast_log(), ast_strdupa, local_pvt::context, local_pvt::exten, LOG_DEBUG, LOG_WARNING, and option_debug.

00199 {
00200    char *exten = ast_strdupa(data);
00201    char *context = NULL, *opts = NULL;
00202    int res;
00203 
00204    if (!(context = strchr(exten, '@'))) {
00205       ast_log(LOG_WARNING, "Someone used Local/%s somewhere without a @context. This is bad.\n", exten);
00206       return AST_DEVICE_INVALID; 
00207    }
00208 
00209    *context++ = '\0';
00210 
00211    /* Strip options if they exist */
00212    if ((opts = strchr(context, '/')))
00213       *opts = '\0';
00214 
00215    if (option_debug > 2)
00216       ast_log(LOG_DEBUG, "Checking if extension %s@%s exists (devicestate)\n", exten, context);
00217    res = ast_exists_extension(NULL, context, exten, 1, NULL);
00218    if (!res)      
00219       return AST_DEVICE_INVALID;
00220    else
00221       return AST_DEVICE_UNKNOWN;
00222 }

static int local_digit_begin ( struct ast_channel ast,
char  digit 
) [static]

Definition at line 476 of file chan_local.c.

References ao2_lock(), ao2_ref(), ao2_unlock(), AST_FRAME_DTMF_BEGIN, f, IS_OUTBOUND, local_queue_frame(), and ast_channel::tech_pvt.

00477 {
00478    struct local_pvt *p = ast->tech_pvt;
00479    int res = -1;
00480    struct ast_frame f = { AST_FRAME_DTMF_BEGIN, };
00481    int isoutbound;
00482 
00483    if (!p)
00484       return -1;
00485 
00486    ao2_ref(p, 1); /* ref for local_queue_frame */
00487    ao2_lock(p);
00488    isoutbound = IS_OUTBOUND(ast, p);
00489    f.subclass = digit;
00490    res = local_queue_frame(p, isoutbound, &f, ast, 0);
00491    ao2_unlock(p);
00492    ao2_ref(p, -1);
00493 
00494    return res;
00495 }

static int local_digit_end ( struct ast_channel ast,
char  digit,
unsigned int  duration 
) [static]

Definition at line 497 of file chan_local.c.

References ao2_lock(), ao2_ref(), ao2_unlock(), AST_FRAME_DTMF_END, f, IS_OUTBOUND, local_queue_frame(), and ast_channel::tech_pvt.

00498 {
00499    struct local_pvt *p = ast->tech_pvt;
00500    int res = -1;
00501    struct ast_frame f = { AST_FRAME_DTMF_END, };
00502    int isoutbound;
00503 
00504    if (!p)
00505       return -1;
00506 
00507    ao2_lock(p);
00508    ao2_ref(p, 1); /* ref for local_queue_frame */
00509    isoutbound = IS_OUTBOUND(ast, p);
00510    f.subclass = digit;
00511    f.len = duration;
00512    res = local_queue_frame(p, isoutbound, &f, ast, 0);
00513    ao2_unlock(p);
00514    ao2_ref(p, -1);
00515 
00516    return res;
00517 }

static int local_fixup ( struct ast_channel oldchan,
struct ast_channel newchan 
) [static]

Definition at line 413 of file chan_local.c.

References ast_channel::_bridge, ao2_lock(), ao2_unlock(), ast_check_hangup(), ast_log(), ast_queue_hangup(), local_pvt::chan, LOG_WARNING, local_pvt::owner, and ast_channel::tech_pvt.

00414 {
00415    struct local_pvt *p = newchan->tech_pvt;
00416 
00417    if (!p)
00418       return -1;
00419 
00420    ao2_lock(p);
00421 
00422    if ((p->owner != oldchan) && (p->chan != oldchan)) {
00423       ast_log(LOG_WARNING, "Old channel wasn't %p but was %p/%p\n", oldchan, p->owner, p->chan);
00424       ao2_unlock(p);
00425       return -1;
00426    }
00427    if (p->owner == oldchan)
00428       p->owner = newchan;
00429    else
00430       p->chan = newchan;
00431 
00432    /* Do not let a masquerade cause a Local channel to be bridged to itself! */
00433    if (!ast_check_hangup(newchan) && ((p->owner && p->owner->_bridge == p->chan) || (p->chan && p->chan->_bridge == p->owner))) {
00434       ast_log(LOG_WARNING, "You can not bridge a Local channel to itself!\n");
00435       ao2_unlock(p);
00436       ast_queue_hangup(newchan);
00437       return -1;
00438    }
00439 
00440    ao2_unlock(p);
00441    return 0;
00442 }

static int local_hangup ( struct ast_channel ast  )  [static]

Hangup a call through the local proxy channel.

Definition at line 626 of file chan_local.c.

References ao2_lock(), ao2_ref(), ao2_trylock(), ao2_unlink(), ao2_unlock(), ast_channel_lock, ast_channel_trylock, ast_channel_unlock, ast_clear_flag, AST_CONTROL_HANGUP, AST_FRAME_CONTROL, ast_hangup(), ast_module_user_remove, ast_queue_hangup(), ast_test_flag, local_pvt::chan, f, ast_channel::hangupcause, IS_OUTBOUND, LOCAL_LAUNCHED_PBX, local_queue_frame(), locals, local_pvt::owner, pbx_builtin_getvar_helper(), pbx_builtin_setvar_helper(), ast_channel::tech_pvt, local_pvt::u_chan, and local_pvt::u_owner.

00627 {
00628    struct local_pvt *p = ast->tech_pvt;
00629    int isoutbound;
00630    struct ast_frame f = { AST_FRAME_CONTROL, AST_CONTROL_HANGUP };
00631    struct ast_channel *ochan = NULL;
00632 
00633    if (!p)
00634       return -1;
00635 
00636    /* we MUST give the tech_pvt a ref here since we are unlocking the
00637     * channel during deadlock avoidance. */
00638    ao2_ref(p, 1);
00639 
00640    while (ao2_trylock(p)) {
00641       ast_channel_unlock(ast);
00642       sched_yield();
00643       ast_channel_lock(ast);
00644    }
00645 
00646    isoutbound = IS_OUTBOUND(ast, p);
00647    if (isoutbound) {
00648       const char *status = pbx_builtin_getvar_helper(p->chan, "DIALSTATUS");
00649       if ((status) && (p->owner)) {
00650          /* Deadlock avoidance */
00651          while (p->owner && ast_channel_trylock(p->owner)) {
00652             ao2_unlock(p);
00653             if (p->chan) {
00654                ast_channel_unlock(p->chan);
00655             }
00656             sched_yield();
00657             if (p->chan) {
00658                ast_channel_lock(p->chan);
00659             }
00660             ao2_lock(p);
00661          }
00662          if (p->owner) {
00663             p->owner->hangupcause = p->chan->hangupcause;
00664             pbx_builtin_setvar_helper(p->owner, "CHANLOCALSTATUS", status);
00665             ast_channel_unlock(p->owner);
00666          }
00667       }
00668       if (!p->chan) {
00669          /* chan was == to ast and was !NULL before deadlock avoidance started, if chan
00670           * is NULL now, then we should bail because that channel
00671           * hungup already. This is possible because we let go of the
00672           * lock given to the ast channel passed to this function during
00673           * deadlock avoidance. */
00674          ao2_unlock(p);
00675          ao2_ref(p, -1);
00676          return 0;
00677       }
00678       p->chan = NULL;
00679       ast_clear_flag(p, LOCAL_LAUNCHED_PBX);
00680       ast_module_user_remove(p->u_chan);
00681    } else {
00682       ast_module_user_remove(p->u_owner);
00683       while (p->chan && ast_channel_trylock(p->chan)) {
00684             ao2_unlock(p);
00685             if (p->owner) {
00686                ast_channel_unlock(p->owner);
00687             }
00688             sched_yield();
00689             if (p->owner) {
00690                ast_channel_lock(p->owner);
00691             }
00692             ao2_lock(p);
00693       }
00694       if (p->chan) {
00695          ast_queue_hangup(p->chan);
00696          ast_channel_unlock(p->chan);
00697       }
00698 
00699       if (!p->owner) {
00700          /* owner was == to ast and was !NULL before deadlock avoidance started, if
00701           * owner is NULL now, then we should bail because that channel
00702           * hungup already. This is possible because we let go of the
00703           * lock given to the ast channel passed to this function during
00704           * deadlock avoidance. */
00705          ao2_unlock(p);
00706          ao2_ref(p, -1);
00707          return 0;
00708       }
00709       p->owner = NULL;
00710    }
00711 
00712    ast->tech_pvt = NULL;
00713 
00714    if (!p->owner && !p->chan) {
00715       ao2_unlock(p);
00716 
00717       /* Remove from list */
00718       ao2_unlink(locals, p);
00719       ao2_ref(p, -1);
00720       return 0;
00721    }
00722    if (p->chan && !ast_test_flag(p, LOCAL_LAUNCHED_PBX)) {
00723       /* Need to actually hangup since there is no PBX */
00724       ochan = p->chan;
00725    } else {
00726       local_queue_frame(p, isoutbound, &f, NULL, 1);
00727    }
00728 
00729    ao2_unlock(p);
00730    if (ochan) {
00731       ast_hangup(ochan);
00732    }
00733 
00734    ao2_ref(p, -1);
00735    return 0;
00736 }

static int local_indicate ( struct ast_channel ast,
int  condition,
const void *  data,
size_t  datalen 
) [static]

Definition at line 444 of file chan_local.c.

References ao2_lock(), ao2_ref(), ao2_unlock(), AST_CONTROL_HOLD, AST_CONTROL_UNHOLD, AST_FRAME_CONTROL, ast_moh_start(), ast_moh_stop(), ast_test_flag, f, IS_OUTBOUND, LOCAL_MOH_PASSTHRU, local_queue_frame(), and ast_channel::tech_pvt.

00445 {
00446    struct local_pvt *p = ast->tech_pvt;
00447    int res = 0;
00448    struct ast_frame f = { AST_FRAME_CONTROL, };
00449    int isoutbound;
00450 
00451    if (!p)
00452       return -1;
00453 
00454    ao2_ref(p, 1); /* ref for local_queue_frame */
00455 
00456    /* If this is an MOH hold or unhold, do it on the Local channel versus real channel */
00457    if (!ast_test_flag(p, LOCAL_MOH_PASSTHRU) && condition == AST_CONTROL_HOLD) {
00458       ast_moh_start(ast, data, NULL);
00459    } else if (!ast_test_flag(p, LOCAL_MOH_PASSTHRU) && condition == AST_CONTROL_UNHOLD) {
00460       ast_moh_stop(ast);
00461    } else {
00462       /* Queue up a frame representing the indication as a control frame */
00463       ao2_lock(p);
00464       isoutbound = IS_OUTBOUND(ast, p);
00465       f.subclass = condition;
00466       f.data = (void*)data;
00467       f.datalen = datalen;
00468       res = local_queue_frame(p, isoutbound, &f, ast, 1);
00469       ao2_unlock(p);
00470    }
00471    ao2_ref(p, -1);
00472 
00473    return res;
00474 }

static struct ast_channel* local_new ( struct local_pvt p,
int  state 
) [static]

Start new local channel.

Definition at line 786 of file chan_local.c.

References ast_channel::accountcode, ast_channel::amaflags, ast_best_codec(), ast_channel_alloc(), ast_channel_free(), ast_copy_string(), ast_log(), ast_module_user_add, ast_random(), AST_STATE_RING, local_pvt::chan, ast_channel::context, local_pvt::context, ast_channel::exten, local_pvt::exten, local_tech, LOG_WARNING, ast_channel::nativeformats, local_pvt::owner, ast_channel::priority, ast_channel::rawreadformat, ast_channel::rawwriteformat, ast_channel::readformat, local_pvt::reqformat, t, ast_channel::tech, ast_channel::tech_pvt, local_pvt::u_chan, local_pvt::u_owner, and ast_channel::writeformat.

Referenced by local_request().

00787 {
00788    struct ast_channel *tmp = NULL, *tmp2 = NULL;
00789    int randnum = ast_random() & 0xffff, fmt = 0;
00790    const char *t;
00791    int ama;
00792 
00793    /* Allocate two new Asterisk channels */
00794    /* safe accountcode */
00795    if (p->owner && p->owner->accountcode)
00796       t = p->owner->accountcode;
00797    else
00798       t = "";
00799 
00800    if (p->owner)
00801       ama = p->owner->amaflags;
00802    else
00803       ama = 0;
00804    if (!(tmp = ast_channel_alloc(1, state, 0, 0, t, p->exten, p->context, ama, "Local/%s@%s-%04x,1", p->exten, p->context, randnum)) 
00805          || !(tmp2 = ast_channel_alloc(1, AST_STATE_RING, 0, 0, t, p->exten, p->context, ama, "Local/%s@%s-%04x,2", p->exten, p->context, randnum))) {
00806       if (tmp)
00807          ast_channel_free(tmp);
00808       if (tmp2)
00809          ast_channel_free(tmp2);
00810       ast_log(LOG_WARNING, "Unable to allocate channel structure(s)\n");
00811       return NULL;
00812    } 
00813 
00814    tmp2->tech = tmp->tech = &local_tech;
00815 
00816    tmp->nativeformats = p->reqformat;
00817    tmp2->nativeformats = p->reqformat;
00818 
00819    /* Determine our read/write format and set it on each channel */
00820    fmt = ast_best_codec(p->reqformat);
00821    tmp->writeformat = fmt;
00822    tmp2->writeformat = fmt;
00823    tmp->rawwriteformat = fmt;
00824    tmp2->rawwriteformat = fmt;
00825    tmp->readformat = fmt;
00826    tmp2->readformat = fmt;
00827    tmp->rawreadformat = fmt;
00828    tmp2->rawreadformat = fmt;
00829 
00830    tmp->tech_pvt = p;
00831    tmp2->tech_pvt = p;
00832 
00833    p->owner = tmp;
00834    p->chan = tmp2;
00835    p->u_owner = ast_module_user_add(p->owner);
00836    p->u_chan = ast_module_user_add(p->chan);
00837 
00838    ast_copy_string(tmp->context, p->context, sizeof(tmp->context));
00839    ast_copy_string(tmp2->context, p->context, sizeof(tmp2->context));
00840    ast_copy_string(tmp2->exten, p->exten, sizeof(tmp->exten));
00841    tmp->priority = 1;
00842    tmp2->priority = 1;
00843 
00844    return tmp;
00845 }

static int local_queue_frame ( struct local_pvt p,
int  isoutbound,
struct ast_frame f,
struct ast_channel us,
int  us_locked 
) [static]

queue a frame on a to either the p->owner or p->chan

Note:
the local_pvt MUST have it's ref count bumped before entering this function and decremented after this function is called. This is a side effect of the deadlock avoidance that is necessary to lock 2 channels and a tech_pvt. Without a ref counted local_pvt, it is impossible to guarantee it will not be destroyed by another thread during deadlock avoidance.

Definition at line 232 of file chan_local.c.

References ao2_lock(), ao2_trylock(), ao2_unlock(), ast_channel_lock, ast_channel_trylock, ast_channel_unlock, AST_CONTROL_RINGING, AST_FRAME_CONTROL, ast_log(), ast_queue_frame(), ast_setstate(), AST_STATE_RINGING, local_pvt::chan, f, ast_channel::generator, LOG_ERROR, and local_pvt::owner.

Referenced by local_answer(), local_digit_begin(), local_digit_end(), local_hangup(), local_indicate(), local_sendhtml(), local_sendtext(), and local_write().

00234 {
00235    struct ast_channel *other = NULL;
00236 
00237    /* Recalculate outbound channel */
00238    other = isoutbound ? p->owner : p->chan;
00239 
00240    if (!other) {
00241       return 0;
00242    }
00243 
00244    /* do not queue frame if generator is on both local channels */
00245    if (us && us->generator && other->generator) {
00246       return 0;
00247    }
00248 
00249    /* Ensure that we have both channels locked */
00250    while (other && ast_channel_trylock(other)) {
00251       int res;
00252       if ((res = ao2_unlock(p))) {
00253          ast_log(LOG_ERROR, "chan_local bug! '&p->lock' was not locked when entering local_queue_frame! (%s)\n", strerror(res));
00254          return -1;
00255       }
00256       if (us && us_locked) {
00257          do {
00258             if (ast_channel_unlock(us)) {
00259                ast_log(LOG_ERROR, "chan_local bug! Our channel was not locked, yet arguments indicated that it was!!\n");
00260                ao2_lock(p);
00261                return -1;
00262             }
00263             usleep(1);
00264             ast_channel_lock(us);
00265          } while (ao2_trylock(p));
00266       } else {
00267          usleep(1);
00268          ao2_lock(p);
00269       }
00270       other = isoutbound ? p->owner : p->chan;
00271    }
00272 
00273    if (other) {
00274       if (f->frametype == AST_FRAME_CONTROL && f->subclass == AST_CONTROL_RINGING) {
00275             ast_setstate(other, AST_STATE_RINGING);
00276       }
00277       ast_queue_frame(other, f);
00278       ast_channel_unlock(other);
00279    }
00280 
00281    return 0;
00282 }

static struct ast_frame * local_read ( struct ast_channel ast  )  [static]

Definition at line 380 of file chan_local.c.

References ast_null_frame.

00381 {
00382    return &ast_null_frame;
00383 }

static struct ast_channel * local_request ( const char *  type,
int  format,
void *  data,
int *  cause 
) [static]

Part of PBX interface.

Definition at line 848 of file chan_local.c.

References ao2_ref(), ao2_unlink(), AST_STATE_DOWN, local_pvt::chan, local_alloc(), local_new(), and locals.

00849 {
00850    struct local_pvt *p = NULL;
00851    struct ast_channel *chan = NULL;
00852 
00853    /* Allocate a new private structure and then Asterisk channel */
00854    if ((p = local_alloc(data, format))) {
00855       if (!(chan = local_new(p, AST_STATE_DOWN))) {
00856          ao2_unlink(locals, p);
00857       }
00858       ao2_ref(p, -1); /* kill the ref from the alloc */
00859    }
00860 
00861    return chan;
00862 }

static int local_sendhtml ( struct ast_channel ast,
int  subclass,
const char *  data,
int  datalen 
) [static]

Definition at line 540 of file chan_local.c.

References ao2_lock(), ao2_ref(), ao2_unlock(), AST_FRAME_HTML, f, IS_OUTBOUND, local_queue_frame(), and ast_channel::tech_pvt.

00541 {
00542    struct local_pvt *p = ast->tech_pvt;
00543    int res = -1;
00544    struct ast_frame f = { AST_FRAME_HTML, };
00545    int isoutbound;
00546 
00547    if (!p)
00548       return -1;
00549    
00550    ao2_lock(p);
00551    ao2_ref(p, 1); /* ref for local_queue_frame */
00552 
00553    isoutbound = IS_OUTBOUND(ast, p);
00554    f.subclass = subclass;
00555    f.data = (char *)data;
00556    f.datalen = datalen;
00557    res = local_queue_frame(p, isoutbound, &f, ast, 0);
00558 
00559    ao2_unlock(p);
00560    ao2_ref(p, -1);
00561 
00562    return res;
00563 }

static int local_sendtext ( struct ast_channel ast,
const char *  text 
) [static]

Definition at line 519 of file chan_local.c.

References ao2_lock(), ao2_ref(), ao2_unlock(), AST_FRAME_TEXT, f, IS_OUTBOUND, local_queue_frame(), and ast_channel::tech_pvt.

00520 {
00521    struct local_pvt *p = ast->tech_pvt;
00522    int res = -1;
00523    struct ast_frame f = { AST_FRAME_TEXT, };
00524    int isoutbound;
00525 
00526    if (!p)
00527       return -1;
00528 
00529    ao2_lock(p);
00530    ao2_ref(p, 1); /* ref for local_queue_frame */
00531    isoutbound = IS_OUTBOUND(ast, p);
00532    f.data = (char *) text;
00533    f.datalen = strlen(text) + 1;
00534    res = local_queue_frame(p, isoutbound, &f, ast, 0);
00535    ao2_unlock(p);
00536    ao2_ref(p, -1);
00537    return res;
00538 }

static int local_setoption ( struct ast_channel chan,
int  option,
void *  data,
int  datalen 
) [static]

Definition at line 132 of file chan_local.c.

References ao2_trylock(), ao2_unlock(), AST_CHAN_WRITE_INFO_T_VERSION, ast_channel_lock, ast_channel_trylock, ast_channel_unlock, ast_log(), AST_OPTION_CHANNEL_WRITE, ast_chan_write_info_t::chan, local_pvt::chan, ast_chan_write_info_t::data, ast_chan_write_info_t::function, LOG_ERROR, LOG_WARNING, ast_channel::name, local_pvt::owner, ast_channel::tech_pvt, ast_chan_write_info_t::value, ast_chan_write_info_t::version, and ast_chan_write_info_t::write_fn.

00133 {
00134    int res;
00135    struct local_pvt *p;
00136    struct ast_channel *otherchan;
00137    ast_chan_write_info_t *write_info;
00138 
00139    if (option != AST_OPTION_CHANNEL_WRITE) {
00140       return -1;
00141    }
00142 
00143    write_info = data;
00144 
00145    if (write_info->version != AST_CHAN_WRITE_INFO_T_VERSION) {
00146       ast_log(LOG_ERROR, "The chan_write_info_t type has changed, and this channel hasn't been updated!\n");
00147       return -1;
00148    }
00149 
00150 
00151 startover:
00152    ast_channel_lock(chan);
00153 
00154    p = chan->tech_pvt;
00155    if (!p) {
00156       ast_channel_unlock(chan);
00157       ast_log(LOG_WARNING, "Could not update other side of %s, local_pvt went away.\n", chan->name);
00158       return -1;
00159    }
00160 
00161    while (ao2_trylock(p)) {
00162       ast_channel_unlock(chan);
00163       sched_yield();
00164       ast_channel_lock(chan);
00165       p = chan->tech_pvt;
00166       if (!p) {
00167          ast_channel_unlock(chan);
00168          ast_log(LOG_WARNING, "Could not update other side of %s, local_pvt went away.\n", chan->name);
00169          return -1;
00170       }
00171    }
00172 
00173    otherchan = (write_info->chan == p->owner) ? p->chan : p->owner;
00174 
00175    if (!otherchan || otherchan == write_info->chan) {
00176       ao2_unlock(p);
00177       ast_channel_unlock(chan);
00178       ast_log(LOG_WARNING, "Could not update other side of %s, other side went away.\n", chan->name);
00179       return 0;
00180    }
00181 
00182    if (ast_channel_trylock(otherchan)) {
00183       ao2_unlock(p);
00184       ast_channel_unlock(chan);
00185       goto startover;
00186    }
00187 
00188    res = write_info->write_fn(otherchan, write_info->function, write_info->data, write_info->value);
00189 
00190    ast_channel_unlock(otherchan);
00191    ao2_unlock(p);
00192    ast_channel_unlock(chan);
00193 
00194    return res;
00195 }

static int local_write ( struct ast_channel ast,
struct ast_frame f 
) [static]

Definition at line 385 of file chan_local.c.

References ao2_lock(), ao2_ref(), ao2_unlock(), AST_FRAME_VIDEO, AST_FRAME_VOICE, ast_log(), ast_test_flag, check_bridge(), f, IS_OUTBOUND, LOCAL_ALREADY_MASQED, local_queue_frame(), LOG_DEBUG, ast_channel::name, option_debug, and ast_channel::tech_pvt.

00386 {
00387    struct local_pvt *p = ast->tech_pvt;
00388    int res = -1;
00389    int isoutbound;
00390 
00391    if (!p)
00392       return -1;
00393 
00394    /* Just queue for delivery to the other side */
00395    ao2_lock(p);
00396    ao2_ref(p, 1); /* ref for local_queue_frame */
00397    isoutbound = IS_OUTBOUND(ast, p);
00398    if (isoutbound && f && (f->frametype == AST_FRAME_VOICE || f->frametype == AST_FRAME_VIDEO))
00399       check_bridge(p);
00400    if (!ast_test_flag(p, LOCAL_ALREADY_MASQED))
00401       res = local_queue_frame(p, isoutbound, f, ast, 1);
00402    else {
00403       if (option_debug)
00404          ast_log(LOG_DEBUG, "Not posting to queue since already masked on '%s'\n", ast->name);
00405       res = 0;
00406    }
00407    ao2_unlock(p);
00408    ao2_ref(p, -1);
00409 
00410    return res;
00411 }

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

Definition at line 901 of file chan_local.c.

Referenced by load_module().

00902 {
00903    return (obj == arg) ? CMP_MATCH : 0;
00904 }

static int locals_show ( int  fd,
int  argc,
char **  argv 
) [static]

CLI command "local show channels".

Definition at line 865 of file chan_local.c.

References ao2_container_count(), ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next(), ao2_lock(), ao2_ref(), ao2_unlock(), ast_cli(), locals, RESULT_SHOWUSAGE, and RESULT_SUCCESS.

00866 {
00867    struct local_pvt *p = NULL;
00868    struct ao2_iterator it;
00869 
00870    if (argc != 3)
00871       return RESULT_SHOWUSAGE;
00872 
00873 
00874    if (ao2_container_count(locals) == 0) {
00875       ast_cli(fd, "No local channels in use\n");
00876       return RESULT_SUCCESS;
00877    }
00878 
00879    it = ao2_iterator_init(locals, 0);
00880    while ((p = ao2_iterator_next(&it))) {
00881       ao2_lock(p);
00882       ast_cli(fd, "%s -- %s@%s\n", p->owner ? p->owner->name : "<unowned>", p->exten, p->context);
00883       ao2_unlock(p);
00884       ao2_ref(p, -1);
00885    }
00886    ao2_iterator_destroy(&it);
00887 
00888    return RESULT_SUCCESS;
00889 }

static int unload_module ( void   )  [static]

Unload the local proxy channel from Asterisk.

Definition at line 925 of file chan_local.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next(), ao2_ref(), ast_channel_unregister(), ast_cli_unregister_multiple(), ast_softhangup(), AST_SOFTHANGUP_APPUNLOAD, cli_local, local_tech, locals, and local_pvt::owner.

00926 {
00927    struct local_pvt *p = NULL;
00928    struct ao2_iterator it;
00929 
00930    /* First, take us out of the channel loop */
00931    ast_cli_unregister_multiple(cli_local, sizeof(cli_local) / sizeof(struct ast_cli_entry));
00932    ast_channel_unregister(&local_tech);
00933 
00934    it = ao2_iterator_init(locals, 0);
00935    while ((p = ao2_iterator_next(&it))) {
00936       if (p->owner) {
00937          ast_softhangup(p->owner, AST_SOFTHANGUP_APPUNLOAD);
00938       }
00939       ao2_ref(p, -1);
00940    }
00941    ao2_iterator_destroy(&it);
00942    ao2_ref(locals, -1);
00943    return 0;
00944 }


Variable Documentation

struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT | AST_MODFLAG_BUILDSUM, .description = "Local Proxy Channel (Note: used internally by other modules)" , .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 = "361d7bb937402d51e4658efb5b4d76e4" , .load = load_module, .unload = unload_module, } [static]

Definition at line 946 of file chan_local.c.

const struct ast_module_info* ast_module_info = &__mod_info [static]

Definition at line 946 of file chan_local.c.

const int BUCKET_SIZE = 1 [static]

Definition at line 73 of file chan_local.c.

struct ast_cli_entry cli_local[] [static]

Initial value:

 {
   { { "local", "show", "channels", NULL },
   locals_show, "List status of local channels",
   show_locals_usage },
}

Definition at line 895 of file chan_local.c.

Referenced by load_module(), and unload_module().

struct ast_channel_tech local_tech [static]

Definition at line 93 of file chan_local.c.

Referenced by load_module(), local_new(), and unload_module().

struct ao2_container* locals [static]

Definition at line 75 of file chan_local.c.

char show_locals_usage[] [static]

Initial value:

 
"Usage: local show channels\n"
"       Provides summary information on active local proxy channels.\n"

Definition at line 891 of file chan_local.c.

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

Definition at line 66 of file chan_local.c.


Generated on Sat Aug 6 00:39:46 2011 for Asterisk - the Open Source PBX by  doxygen 1.4.7