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

Generated on Tue Apr 28 22:50:03 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7