Fri Jul 24 00:40:48 2009

Asterisk developer's documentation


chan_local.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2005, Digium, Inc.
00005  *
00006  * Mark Spencer <markster@digium.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*! \file
00020  *
00021  * \author Mark Spencer <markster@digium.com>
00022  *
00023  * \brief Local Proxy Channel
00024  * 
00025  * \ingroup channel_drivers
00026  */
00027 
00028 #include "asterisk.h"
00029 
00030 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 182280 $")
00031 
00032 #include <fcntl.h>
00033 #include <sys/signal.h>
00034 
00035 #include "asterisk/lock.h"
00036 #include "asterisk/channel.h"
00037 #include "asterisk/config.h"
00038 #include "asterisk/module.h"
00039 #include "asterisk/pbx.h"
00040 #include "asterisk/sched.h"
00041 #include "asterisk/io.h"
00042 #include "asterisk/rtp.h"
00043 #include "asterisk/acl.h"
00044 #include "asterisk/callerid.h"
00045 #include "asterisk/file.h"
00046 #include "asterisk/cli.h"
00047 #include "asterisk/app.h"
00048 #include "asterisk/musiconhold.h"
00049 #include "asterisk/manager.h"
00050 #include "asterisk/stringfields.h"
00051 #include "asterisk/devicestate.h"
00052 
00053 static const char tdesc[] = "Local Proxy Channel Driver";
00054 
00055 #define IS_OUTBOUND(a,b) (a == b->chan ? 1 : 0)
00056 
00057 static struct ast_jb_conf g_jb_conf = {
00058    .flags = 0,
00059    .max_size = -1,
00060    .resync_threshold = -1,
00061    .impl = "",
00062 };
00063 
00064 static struct ast_channel *local_request(const char *type, int format, void *data, int *cause);
00065 static int local_digit_begin(struct ast_channel *ast, char digit);
00066 static int local_digit_end(struct ast_channel *ast, char digit, unsigned int duration);
00067 static int local_call(struct ast_channel *ast, char *dest, int timeout);
00068 static int local_hangup(struct ast_channel *ast);
00069 static int local_answer(struct ast_channel *ast);
00070 static struct ast_frame *local_read(struct ast_channel *ast);
00071 static int local_write(struct ast_channel *ast, struct ast_frame *f);
00072 static int local_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen);
00073 static int local_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
00074 static int local_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen);
00075 static int local_sendtext(struct ast_channel *ast, const char *text);
00076 static int local_devicestate(void *data);
00077 static struct ast_channel *local_bridgedchannel(struct ast_channel *chan, struct ast_channel *bridge);
00078 
00079 /* PBX interface structure for channel registration */
00080 static const struct ast_channel_tech local_tech = {
00081    .type = "Local",
00082    .description = tdesc,
00083    .capabilities = -1,
00084    .requester = local_request,
00085    .send_digit_begin = local_digit_begin,
00086    .send_digit_end = local_digit_end,
00087    .call = local_call,
00088    .hangup = local_hangup,
00089    .answer = local_answer,
00090    .read = local_read,
00091    .write = local_write,
00092    .write_video = local_write,
00093    .exception = local_read,
00094    .indicate = local_indicate,
00095    .fixup = local_fixup,
00096    .send_html = local_sendhtml,
00097    .send_text = local_sendtext,
00098    .devicestate = local_devicestate,
00099    .bridged_channel = local_bridgedchannel,
00100 };
00101 
00102 struct local_pvt {
00103    ast_mutex_t lock;       /* Channel private lock */
00104    unsigned int flags;                     /* Private flags */
00105    char context[AST_MAX_CONTEXT];      /* Context to call */
00106    char exten[AST_MAX_EXTENSION];      /* Extension to call */
00107    int reqformat;          /* Requested format */
00108    struct ast_jb_conf jb_conf;      /*!< jitterbuffer configuration for this local channel */
00109    struct ast_channel *owner;    /* Master Channel - Bridging happens here */
00110    struct ast_channel *chan;     /* Outbound channel - PBX is run here */
00111    struct ast_module_user *u_owner; /*! reference to keep the module loaded while in use */
00112    struct ast_module_user *u_chan;     /*! reference to keep the module loaded while in use */
00113    AST_LIST_ENTRY(local_pvt) list;     /* Next entity */
00114 };
00115 
00116 #define LOCAL_GLARE_DETECT    (1 << 0) /*!< Detect glare on hangup */
00117 #define LOCAL_CANCEL_QUEUE    (1 << 1) /*!< Cancel queue */
00118 #define LOCAL_ALREADY_MASQED  (1 << 2) /*!< Already masqueraded */
00119 #define LOCAL_LAUNCHED_PBX    (1 << 3) /*!< PBX was launched */
00120 #define LOCAL_NO_OPTIMIZATION (1 << 4) /*!< Do not optimize using masquerading */
00121 #define LOCAL_BRIDGE          (1 << 5) /*!< Report back the "true" channel as being bridged to */
00122 
00123 static AST_LIST_HEAD_STATIC(locals, local_pvt);
00124 
00125 /*! \brief Adds devicestate to local channels */
00126 static int local_devicestate(void *data)
00127 {
00128    char *exten = ast_strdupa(data);
00129    char *context = NULL, *opts = NULL;
00130    int res;
00131    struct local_pvt *lp;
00132 
00133    if (!(context = strchr(exten, '@'))) {
00134       ast_log(LOG_WARNING, "Someone used Local/%s somewhere without a @context. This is bad.\n", exten);
00135       return AST_DEVICE_INVALID; 
00136    }
00137 
00138    *context++ = '\0';
00139 
00140    /* Strip options if they exist */
00141    if ((opts = strchr(context, '/')))
00142       *opts = '\0';
00143 
00144    ast_debug(3, "Checking if extension %s@%s exists (devicestate)\n", exten, context);
00145 
00146    res = ast_exists_extension(NULL, context, exten, 1, NULL);
00147    if (!res)      
00148       return AST_DEVICE_INVALID;
00149    
00150    res = AST_DEVICE_NOT_INUSE;
00151    AST_LIST_LOCK(&locals);
00152    AST_LIST_TRAVERSE(&locals, lp, list) {
00153       if (!strcmp(exten, lp->exten) && !strcmp(context, lp->context) && lp->owner) {
00154          res = AST_DEVICE_INUSE;
00155          break;
00156       }
00157    }
00158    AST_LIST_UNLOCK(&locals);
00159 
00160    return res;
00161 }
00162 
00163 /*!
00164  * \note Assumes the pvt is no longer in the pvts list
00165  */
00166 static struct local_pvt *local_pvt_destroy(struct local_pvt *pvt)
00167 {
00168    ast_mutex_destroy(&pvt->lock);
00169    ast_free(pvt);
00170    return NULL;
00171 }
00172 
00173 /*! \brief Return the bridged channel of a Local channel */
00174 static struct ast_channel *local_bridgedchannel(struct ast_channel *chan, struct ast_channel *bridge)
00175 {
00176    struct local_pvt *p = bridge->tech_pvt;
00177    struct ast_channel *bridged = bridge;
00178 
00179    ast_mutex_lock(&p->lock);
00180 
00181    if (ast_test_flag(p, LOCAL_BRIDGE)) {
00182       /* Find the opposite channel */
00183       bridged = (bridge == p->owner ? p->chan : p->owner);
00184       
00185       /* Now see if the opposite channel is bridged to anything */
00186       if (!bridged) {
00187          bridged = bridge;
00188       } else if (bridged->_bridge) {
00189          bridged = bridged->_bridge;
00190       }
00191    }
00192 
00193    ast_mutex_unlock(&p->lock);
00194 
00195    return bridged;
00196 }
00197 
00198 static int local_queue_frame(struct local_pvt *p, int isoutbound, struct ast_frame *f, 
00199    struct ast_channel *us, int us_locked)
00200 {
00201    struct ast_channel *other = NULL;
00202 
00203    /* Recalculate outbound channel */
00204    other = isoutbound ? p->owner : p->chan;
00205 
00206    if (!other) {
00207       return 0;
00208    }
00209 
00210    /* do not queue frame if generator is on both local channels */
00211    if (us && us->generator && other->generator) {
00212       return 0;
00213    }
00214 
00215    /* Set glare detection */
00216    ast_set_flag(p, LOCAL_GLARE_DETECT);
00217 
00218    /* Ensure that we have both channels locked */
00219    while (other && ast_channel_trylock(other)) {
00220       ast_mutex_unlock(&p->lock);
00221       if (us && us_locked) {
00222          do {
00223             CHANNEL_DEADLOCK_AVOIDANCE(us);
00224          } while (ast_mutex_trylock(&p->lock));
00225       } else {
00226          usleep(1);
00227          ast_mutex_lock(&p->lock);
00228       }
00229       other = isoutbound ? p->owner : p->chan;
00230    }
00231 
00232    /* Since glare detection only occurs within this function, and because
00233     * a pvt flag cannot be set without having the pvt lock, this is the only
00234     * location where we could detect a cancelling of the queue. */
00235    if (ast_test_flag(p, LOCAL_CANCEL_QUEUE)) {
00236       /* We had a glare on the hangup.  Forget all this business,
00237       return and destroy p.  */
00238       ast_mutex_unlock(&p->lock);
00239       p = local_pvt_destroy(p);
00240       return -1;
00241    }
00242 
00243    if (other) {
00244       ast_queue_frame(other, f);
00245       ast_channel_unlock(other);
00246    }
00247 
00248    ast_clear_flag(p, LOCAL_GLARE_DETECT);
00249 
00250    return 0;
00251 }
00252 
00253 static int local_answer(struct ast_channel *ast)
00254 {
00255    struct local_pvt *p = ast->tech_pvt;
00256    int isoutbound;
00257    int res = -1;
00258 
00259    if (!p)
00260       return -1;
00261 
00262    ast_mutex_lock(&p->lock);
00263    isoutbound = IS_OUTBOUND(ast, p);
00264    if (isoutbound) {
00265       /* Pass along answer since somebody answered us */
00266       struct ast_frame answer = { AST_FRAME_CONTROL, AST_CONTROL_ANSWER };
00267       res = local_queue_frame(p, isoutbound, &answer, ast, 1);
00268    } else
00269       ast_log(LOG_WARNING, "Huh?  Local is being asked to answer?\n");
00270    if (!res)
00271       ast_mutex_unlock(&p->lock);
00272    return res;
00273 }
00274 
00275 static void check_bridge(struct local_pvt *p, int isoutbound)
00276 {
00277    struct ast_channel_monitor *tmp;
00278    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)))
00279       return;
00280 
00281    /* only do the masquerade if we are being called on the outbound channel,
00282       if it has been bridged to another channel and if there are no pending
00283       frames on the owner channel (because they would be transferred to the
00284       outbound channel during the masquerade)
00285    */
00286    if (isoutbound && p->chan->_bridge /* Not ast_bridged_channel!  Only go one step! */ && AST_LIST_EMPTY(&p->owner->readq)) {
00287       /* Masquerade bridged channel into owner */
00288       /* Lock everything we need, one by one, and give up if
00289          we can't get everything.  Remember, we'll get another
00290          chance in just a little bit */
00291       if (!ast_channel_trylock(p->chan->_bridge)) {
00292          if (!ast_check_hangup(p->chan->_bridge)) {
00293             if (!ast_channel_trylock(p->owner)) {
00294                if (!ast_check_hangup(p->owner)) {
00295                   if (p->owner->monitor && !p->chan->_bridge->monitor) {
00296                      /* If a local channel is being monitored, we don't want a masquerade
00297                       * to cause the monitor to go away. Since the masquerade swaps the monitors,
00298                       * pre-swapping the monitors before the masquerade will ensure that the monitor
00299                       * ends up where it is expected.
00300                       */
00301                      tmp = p->owner->monitor;
00302                      p->owner->monitor = p->chan->_bridge->monitor;
00303                      p->chan->_bridge->monitor = tmp;
00304                   }
00305                   if (p->chan->audiohooks) {
00306                      struct ast_audiohook_list *audiohooks_swapper;
00307                      audiohooks_swapper = p->chan->audiohooks;
00308                      p->chan->audiohooks = p->owner->audiohooks;
00309                      p->owner->audiohooks = audiohooks_swapper;
00310                   }
00311                   ast_app_group_update(p->chan, p->owner);
00312                   ast_channel_masquerade(p->owner, p->chan->_bridge);
00313                   ast_set_flag(p, LOCAL_ALREADY_MASQED);
00314                }
00315                ast_channel_unlock(p->owner);
00316             }
00317             ast_channel_unlock(p->chan->_bridge);
00318          }
00319       }
00320    /* We only allow masquerading in one 'direction'... it's important to preserve the state
00321       (group variables, etc.) that live on p->chan->_bridge (and were put there by the dialplan)
00322       when the local channels go away.
00323    */
00324 #if 0
00325    } else if (!isoutbound && p->owner && p->owner->_bridge && p->chan && AST_LIST_EMPTY(&p->chan->readq)) {
00326       /* Masquerade bridged channel into chan */
00327       if (!ast_mutex_trylock(&(p->owner->_bridge)->lock)) {
00328          if (!ast_check_hangup(p->owner->_bridge)) {
00329             if (!ast_mutex_trylock(&p->chan->lock)) {
00330                if (!ast_check_hangup(p->chan)) {
00331                   ast_channel_masquerade(p->chan, p->owner->_bridge);
00332                   ast_set_flag(p, LOCAL_ALREADY_MASQED);
00333                }
00334                ast_mutex_unlock(&p->chan->lock);
00335             }
00336          }
00337          ast_mutex_unlock(&(p->owner->_bridge)->lock);
00338       }
00339 #endif
00340    }
00341 }
00342 
00343 static struct ast_frame  *local_read(struct ast_channel *ast)
00344 {
00345    return &ast_null_frame;
00346 }
00347 
00348 static int local_write(struct ast_channel *ast, struct ast_frame *f)
00349 {
00350    struct local_pvt *p = ast->tech_pvt;
00351    int res = -1;
00352    int isoutbound;
00353 
00354    if (!p)
00355       return -1;
00356 
00357    /* Just queue for delivery to the other side */
00358    ast_mutex_lock(&p->lock);
00359    isoutbound = IS_OUTBOUND(ast, p);
00360    if (f && (f->frametype == AST_FRAME_VOICE || f->frametype == AST_FRAME_VIDEO))
00361       check_bridge(p, isoutbound);
00362    if (!ast_test_flag(p, LOCAL_ALREADY_MASQED))
00363       res = local_queue_frame(p, isoutbound, f, ast, 1);
00364    else {
00365       ast_debug(1, "Not posting to queue since already masked on '%s'\n", ast->name);
00366       res = 0;
00367    }
00368    if (!res)
00369       ast_mutex_unlock(&p->lock);
00370    return res;
00371 }
00372 
00373 static int local_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
00374 {
00375    struct local_pvt *p = newchan->tech_pvt;
00376 
00377    if (!p)
00378       return -1;
00379 
00380    ast_mutex_lock(&p->lock);
00381 
00382    if ((p->owner != oldchan) && (p->chan != oldchan)) {
00383       ast_log(LOG_WARNING, "Old channel wasn't %p but was %p/%p\n", oldchan, p->owner, p->chan);
00384       ast_mutex_unlock(&p->lock);
00385       return -1;
00386    }
00387    if (p->owner == oldchan)
00388       p->owner = newchan;
00389    else
00390       p->chan = newchan;
00391    ast_mutex_unlock(&p->lock);
00392    return 0;
00393 }
00394 
00395 static int local_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen)
00396 {
00397    struct local_pvt *p = ast->tech_pvt;
00398    int res = 0;
00399    struct ast_frame f = { AST_FRAME_CONTROL, };
00400    int isoutbound;
00401 
00402    if (!p)
00403       return -1;
00404 
00405    /* If this is an MOH hold or unhold, do it on the Local channel versus real channel */
00406    if (condition == AST_CONTROL_HOLD) {
00407       ast_moh_start(ast, data, NULL);
00408    } else if (condition == AST_CONTROL_UNHOLD) {
00409       ast_moh_stop(ast);
00410    } else {
00411       /* Queue up a frame representing the indication as a control frame */
00412       ast_mutex_lock(&p->lock);
00413       isoutbound = IS_OUTBOUND(ast, p);
00414       f.subclass = condition;
00415       f.data.ptr = (void*)data;
00416       f.datalen = datalen;
00417       if (!(res = local_queue_frame(p, isoutbound, &f, ast, 1)))
00418          ast_mutex_unlock(&p->lock);
00419    }
00420 
00421    return res;
00422 }
00423 
00424 static int local_digit_begin(struct ast_channel *ast, char digit)
00425 {
00426    struct local_pvt *p = ast->tech_pvt;
00427    int res = -1;
00428    struct ast_frame f = { AST_FRAME_DTMF_BEGIN, };
00429    int isoutbound;
00430 
00431    if (!p)
00432       return -1;
00433 
00434    ast_mutex_lock(&p->lock);
00435    isoutbound = IS_OUTBOUND(ast, p);
00436    f.subclass = digit;
00437    if (!(res = local_queue_frame(p, isoutbound, &f, ast, 0)))
00438       ast_mutex_unlock(&p->lock);
00439 
00440    return res;
00441 }
00442 
00443 static int local_digit_end(struct ast_channel *ast, char digit, unsigned int duration)
00444 {
00445    struct local_pvt *p = ast->tech_pvt;
00446    int res = -1;
00447    struct ast_frame f = { AST_FRAME_DTMF_END, };
00448    int isoutbound;
00449 
00450    if (!p)
00451       return -1;
00452 
00453    ast_mutex_lock(&p->lock);
00454    isoutbound = IS_OUTBOUND(ast, p);
00455    f.subclass = digit;
00456    f.len = duration;
00457    if (!(res = local_queue_frame(p, isoutbound, &f, ast, 0)))
00458       ast_mutex_unlock(&p->lock);
00459 
00460    return res;
00461 }
00462 
00463 static int local_sendtext(struct ast_channel *ast, const char *text)
00464 {
00465    struct local_pvt *p = ast->tech_pvt;
00466    int res = -1;
00467    struct ast_frame f = { AST_FRAME_TEXT, };
00468    int isoutbound;
00469 
00470    if (!p)
00471       return -1;
00472 
00473    ast_mutex_lock(&p->lock);
00474    isoutbound = IS_OUTBOUND(ast, p);
00475    f.data.ptr = (char *) text;
00476    f.datalen = strlen(text) + 1;
00477    if (!(res = local_queue_frame(p, isoutbound, &f, ast, 0)))
00478       ast_mutex_unlock(&p->lock);
00479    return res;
00480 }
00481 
00482 static int local_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen)
00483 {
00484    struct local_pvt *p = ast->tech_pvt;
00485    int res = -1;
00486    struct ast_frame f = { AST_FRAME_HTML, };
00487    int isoutbound;
00488 
00489    if (!p)
00490       return -1;
00491    
00492    ast_mutex_lock(&p->lock);
00493    isoutbound = IS_OUTBOUND(ast, p);
00494    f.subclass = subclass;
00495    f.data.ptr = (char *)data;
00496    f.datalen = datalen;
00497    if (!(res = local_queue_frame(p, isoutbound, &f, ast, 0)))
00498       ast_mutex_unlock(&p->lock);
00499    return res;
00500 }
00501 
00502 /*! \brief Initiate new call, part of PBX interface 
00503  *    dest is the dial string */
00504 static int local_call(struct ast_channel *ast, char *dest, int timeout)
00505 {
00506    struct local_pvt *p = ast->tech_pvt;
00507    int res;
00508    struct ast_var_t *varptr = NULL, *new;
00509    size_t len, namelen;
00510 
00511    if (!p)
00512       return -1;
00513    
00514    ast_mutex_lock(&p->lock);
00515 
00516    /*
00517     * Note that cid_num and cid_name aren't passed in the ast_channel_alloc
00518     * call, so it's done here instead.
00519     */
00520    p->chan->cid.cid_dnid = ast_strdup(p->owner->cid.cid_dnid);
00521    p->chan->cid.cid_num = ast_strdup(p->owner->cid.cid_num);
00522    p->chan->cid.cid_name = ast_strdup(p->owner->cid.cid_name);
00523    p->chan->cid.cid_rdnis = ast_strdup(p->owner->cid.cid_rdnis);
00524    p->chan->cid.cid_ani = ast_strdup(p->owner->cid.cid_ani);
00525    p->chan->cid.cid_pres = p->owner->cid.cid_pres;
00526    p->chan->cid.cid_ani2 = p->owner->cid.cid_ani2;
00527    p->chan->cid.cid_ton = p->owner->cid.cid_ton;
00528    p->chan->cid.cid_tns = p->owner->cid.cid_tns;
00529    ast_string_field_set(p->chan, language, p->owner->language);
00530    ast_string_field_set(p->chan, accountcode, p->owner->accountcode);
00531    ast_string_field_set(p->chan, musicclass, p->owner->musicclass);
00532    ast_cdr_update(p->chan);
00533    p->chan->cdrflags = p->owner->cdrflags;
00534 
00535    if (!ast_exists_extension(NULL, p->chan->context, p->chan->exten, 1, p->owner->cid.cid_num)) {
00536       ast_log(LOG_NOTICE, "No such extension/context %s@%s while calling Local channel\n", p->chan->exten, p->chan->context);
00537       ast_mutex_unlock(&p->lock);
00538       return -1;
00539    }
00540 
00541    /* copy the channel variables from the incoming channel to the outgoing channel */
00542    /* Note that due to certain assumptions, they MUST be in the same order */
00543    AST_LIST_TRAVERSE(&p->owner->varshead, varptr, entries) {
00544       namelen = strlen(varptr->name);
00545       len = sizeof(struct ast_var_t) + namelen + strlen(varptr->value) + 2;
00546       if ((new = ast_calloc(1, len))) {
00547          memcpy(new, varptr, len);
00548          new->value = &(new->name[0]) + namelen + 1;
00549          AST_LIST_INSERT_TAIL(&p->chan->varshead, new, entries);
00550       }
00551    }
00552    ast_channel_datastore_inherit(p->owner, p->chan);
00553 
00554    /* Start switch on sub channel */
00555    if (!(res = ast_pbx_start(p->chan)))
00556       ast_set_flag(p, LOCAL_LAUNCHED_PBX);
00557 
00558    ast_mutex_unlock(&p->lock);
00559    return res;
00560 }
00561 
00562 /*! \brief Hangup a call through the local proxy channel */
00563 static int local_hangup(struct ast_channel *ast)
00564 {
00565    struct local_pvt *p = ast->tech_pvt;
00566    int isoutbound;
00567    struct ast_frame f = { AST_FRAME_CONTROL, AST_CONTROL_HANGUP, .data.uint32 = ast->hangupcause };
00568    struct ast_channel *ochan = NULL;
00569    int glaredetect = 0, res = 0;
00570 
00571    if (!p)
00572       return -1;
00573 
00574    ast_mutex_lock(&p->lock);
00575 
00576    if (p->chan && ast_test_flag(ast, AST_FLAG_ANSWERED_ELSEWHERE)) 
00577       ast_set_flag(p->chan, AST_FLAG_ANSWERED_ELSEWHERE);
00578    isoutbound = IS_OUTBOUND(ast, p);
00579    if (isoutbound) {
00580       const char *status = pbx_builtin_getvar_helper(p->chan, "DIALSTATUS");
00581       if ((status) && (p->owner)) {
00582          /* Deadlock avoidance */
00583          while (p->owner && ast_channel_trylock(p->owner)) {
00584             ast_mutex_unlock(&p->lock);
00585             if (ast) {
00586                ast_channel_unlock(ast);
00587             }
00588             usleep(1);
00589             if (ast) {
00590                ast_channel_lock(ast);
00591             }
00592             ast_mutex_lock(&p->lock);
00593          }
00594          if (p->owner) {
00595             pbx_builtin_setvar_helper(p->owner, "CHANLOCALSTATUS", status);
00596             ast_channel_unlock(p->owner);
00597          }
00598       }
00599       p->chan = NULL;
00600       ast_clear_flag(p, LOCAL_LAUNCHED_PBX);
00601       ast_module_user_remove(p->u_chan);
00602    } else {
00603       p->owner = NULL;
00604       ast_module_user_remove(p->u_owner);
00605       while (p->chan && ast_channel_trylock(p->chan)) {
00606          DEADLOCK_AVOIDANCE(&p->lock);
00607       }
00608       if (p->chan) {
00609          ast_queue_hangup(p->chan);
00610          ast_channel_unlock(p->chan);
00611       }
00612    }
00613    
00614    ast->tech_pvt = NULL;
00615    
00616    if (!p->owner && !p->chan) {
00617       /* Okay, done with the private part now, too. */
00618       glaredetect = ast_test_flag(p, LOCAL_GLARE_DETECT);
00619       /* If we have a queue holding, don't actually destroy p yet, but
00620          let local_queue do it. */
00621       if (glaredetect)
00622          ast_set_flag(p, LOCAL_CANCEL_QUEUE);
00623       ast_mutex_unlock(&p->lock);
00624       /* Remove from list */
00625       AST_LIST_LOCK(&locals);
00626       AST_LIST_REMOVE(&locals, p, list);
00627       AST_LIST_UNLOCK(&locals);
00628       /* Grab / release lock just in case */
00629       ast_mutex_lock(&p->lock);
00630       ast_mutex_unlock(&p->lock);
00631       /* And destroy */
00632       if (!glaredetect) {
00633          p = local_pvt_destroy(p);
00634       }
00635       return 0;
00636    }
00637    if (p->chan && !ast_test_flag(p, LOCAL_LAUNCHED_PBX))
00638       /* Need to actually hangup since there is no PBX */
00639       ochan = p->chan;
00640    else
00641       res = local_queue_frame(p, isoutbound, &f, NULL, 1);
00642    if (!res)
00643       ast_mutex_unlock(&p->lock);
00644    if (ochan)
00645       ast_hangup(ochan);
00646    return 0;
00647 }
00648 
00649 /*! \brief Create a call structure */
00650 static struct local_pvt *local_alloc(const char *data, int format)
00651 {
00652    struct local_pvt *tmp = NULL;
00653    char *c = NULL, *opts = NULL;
00654 
00655    if (!(tmp = ast_calloc(1, sizeof(*tmp))))
00656       return NULL;
00657 
00658    /* Initialize private structure information */
00659    ast_mutex_init(&tmp->lock);
00660    ast_copy_string(tmp->exten, data, sizeof(tmp->exten));
00661 
00662    memcpy(&tmp->jb_conf, &g_jb_conf, sizeof(tmp->jb_conf));
00663 
00664    /* Look for options */
00665    if ((opts = strchr(tmp->exten, '/'))) {
00666       *opts++ = '\0';
00667       if (strchr(opts, 'n'))
00668          ast_set_flag(tmp, LOCAL_NO_OPTIMIZATION);
00669       if (strchr(opts, 'j')) {
00670          if (ast_test_flag(tmp, LOCAL_NO_OPTIMIZATION))
00671             ast_set_flag(&tmp->jb_conf, AST_JB_ENABLED);
00672          else {
00673             ast_log(LOG_ERROR, "You must use the 'n' option for chan_local "
00674                "to use the 'j' option to enable the jitterbuffer\n");
00675          }
00676       }
00677       if (strchr(opts, 'b')) {
00678          ast_set_flag(tmp, LOCAL_BRIDGE);
00679       }
00680    }
00681 
00682    /* Look for a context */
00683    if ((c = strchr(tmp->exten, '@')))
00684       *c++ = '\0';
00685 
00686    ast_copy_string(tmp->context, c ? c : "default", sizeof(tmp->context));
00687 
00688    tmp->reqformat = format;
00689 
00690 #if 0
00691    /* We can't do this check here, because we don't know the CallerID yet, and
00692     * the CallerID could potentially affect what step is actually taken (or
00693     * even if that step exists). */
00694    if (!ast_exists_extension(NULL, tmp->context, tmp->exten, 1, NULL)) {
00695       ast_log(LOG_NOTICE, "No such extension/context %s@%s creating local channel\n", tmp->exten, tmp->context);
00696       tmp = local_pvt_destroy(tmp);
00697    } else {
00698 #endif
00699       /* Add to list */
00700       AST_LIST_LOCK(&locals);
00701       AST_LIST_INSERT_HEAD(&locals, tmp, list);
00702       AST_LIST_UNLOCK(&locals);
00703 #if 0
00704    }
00705 #endif
00706    
00707    return tmp;
00708 }
00709 
00710 /*! \brief Start new local channel */
00711 static struct ast_channel *local_new(struct local_pvt *p, int state)
00712 {
00713    struct ast_channel *tmp = NULL, *tmp2 = NULL;
00714    int randnum = ast_random() & 0xffff, fmt = 0;
00715    const char *t;
00716    int ama;
00717 
00718    /* Allocate two new Asterisk channels */
00719    /* safe accountcode */
00720    if (p->owner && p->owner->accountcode)
00721       t = p->owner->accountcode;
00722    else
00723       t = "";
00724 
00725    if (p->owner)
00726       ama = p->owner->amaflags;
00727    else
00728       ama = 0;
00729    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)) 
00730          || !(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))) {
00731       if (tmp)
00732          ast_channel_free(tmp);
00733       if (tmp2)
00734          ast_channel_free(tmp2);
00735       ast_log(LOG_WARNING, "Unable to allocate channel structure(s)\n");
00736       return NULL;
00737    } 
00738 
00739    tmp2->tech = tmp->tech = &local_tech;
00740 
00741    tmp->nativeformats = p->reqformat;
00742    tmp2->nativeformats = p->reqformat;
00743 
00744    /* Determine our read/write format and set it on each channel */
00745    fmt = ast_best_codec(p->reqformat);
00746    tmp->writeformat = fmt;
00747    tmp2->writeformat = fmt;
00748    tmp->rawwriteformat = fmt;
00749    tmp2->rawwriteformat = fmt;
00750    tmp->readformat = fmt;
00751    tmp2->readformat = fmt;
00752    tmp->rawreadformat = fmt;
00753    tmp2->rawreadformat = fmt;
00754 
00755    tmp->tech_pvt = p;
00756    tmp2->tech_pvt = p;
00757 
00758    p->owner = tmp;
00759    p->chan = tmp2;
00760    p->u_owner = ast_module_user_add(p->owner);
00761    p->u_chan = ast_module_user_add(p->chan);
00762 
00763    ast_copy_string(tmp->context, p->context, sizeof(tmp->context));
00764    ast_copy_string(tmp2->context, p->context, sizeof(tmp2->context));
00765    ast_copy_string(tmp2->exten, p->exten, sizeof(tmp->exten));
00766    tmp->priority = 1;
00767    tmp2->priority = 1;
00768 
00769    ast_jb_configure(tmp, &p->jb_conf);
00770 
00771    return tmp;
00772 }
00773 
00774 /*! \brief Part of PBX interface */
00775 static struct ast_channel *local_request(const char *type, int format, void *data, int *cause)
00776 {
00777    struct local_pvt *p = NULL;
00778    struct ast_channel *chan = NULL;
00779 
00780    /* Allocate a new private structure and then Asterisk channel */
00781    if ((p = local_alloc(data, format))) {
00782       if (!(chan = local_new(p, AST_STATE_DOWN))) {
00783          AST_LIST_LOCK(&locals);
00784          AST_LIST_REMOVE(&locals, p, list);
00785          AST_LIST_UNLOCK(&locals);
00786          p = local_pvt_destroy(p);
00787       }
00788    }
00789 
00790    return chan;
00791 }
00792 
00793 /*! \brief CLI command "local show channels" */
00794 static char *locals_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00795 {
00796    struct local_pvt *p = NULL;
00797 
00798    switch (cmd) {
00799    case CLI_INIT:
00800       e->command = "local show channels";
00801       e->usage =
00802          "Usage: local show channels\n"
00803          "       Provides summary information on active local proxy channels.\n";
00804       return NULL;
00805    case CLI_GENERATE:
00806       return NULL;
00807    }
00808 
00809    if (a->argc != 3)
00810       return CLI_SHOWUSAGE;
00811 
00812    AST_LIST_LOCK(&locals);
00813    if (!AST_LIST_EMPTY(&locals)) {
00814       AST_LIST_TRAVERSE(&locals, p, list) {
00815          ast_mutex_lock(&p->lock);
00816          ast_cli(a->fd, "%s -- %s@%s\n", p->owner ? p->owner->name : "<unowned>", p->exten, p->context);
00817          ast_mutex_unlock(&p->lock);
00818       }
00819    } else
00820       ast_cli(a->fd, "No local channels in use\n");
00821    AST_LIST_UNLOCK(&locals);
00822 
00823    return CLI_SUCCESS;
00824 }
00825 
00826 static struct ast_cli_entry cli_local[] = {
00827    AST_CLI_DEFINE(locals_show, "List status of local channels"),
00828 };
00829 
00830 /*! \brief Load module into PBX, register channel */
00831 static int load_module(void)
00832 {
00833    /* Make sure we can register our channel type */
00834    if (ast_channel_register(&local_tech)) {
00835       ast_log(LOG_ERROR, "Unable to register channel class 'Local'\n");
00836       return AST_MODULE_LOAD_FAILURE;
00837    }
00838    ast_cli_register_multiple(cli_local, sizeof(cli_local) / sizeof(struct ast_cli_entry));
00839    return AST_MODULE_LOAD_SUCCESS;
00840 }
00841 
00842 /*! \brief Unload the local proxy channel from Asterisk */
00843 static int unload_module(void)
00844 {
00845    struct local_pvt *p = NULL;
00846 
00847    /* First, take us out of the channel loop */
00848    ast_cli_unregister_multiple(cli_local, sizeof(cli_local) / sizeof(struct ast_cli_entry));
00849    ast_channel_unregister(&local_tech);
00850    if (!AST_LIST_LOCK(&locals)) {
00851       /* Hangup all interfaces if they have an owner */
00852       AST_LIST_TRAVERSE(&locals, p, list) {
00853          if (p->owner)
00854             ast_softhangup(p->owner, AST_SOFTHANGUP_APPUNLOAD);
00855       }
00856       AST_LIST_UNLOCK(&locals);
00857    } else {
00858       ast_log(LOG_WARNING, "Unable to lock the monitor\n");
00859       return -1;
00860    }     
00861    return 0;
00862 }
00863 
00864 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Local Proxy Channel (Note: used internally by other modules)");

Generated on Fri Jul 24 00:40:49 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7