Sat Nov 1 06:28:25 2008

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: 152922 $")
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    /* do not queue frame if generator is on both local channels */
00172    if (us && us->generator && other->generator)
00173       return 0;
00174 
00175    /* Set glare detection */
00176    ast_set_flag(p, LOCAL_GLARE_DETECT);
00177    if (ast_test_flag(p, LOCAL_CANCEL_QUEUE)) {
00178       /* We had a glare on the hangup.  Forget all this business,
00179       return and destroy p.  */
00180       ast_mutex_unlock(&p->lock);
00181       p = local_pvt_destroy(p);
00182       return -1;
00183    }
00184    if (!other) {
00185       ast_clear_flag(p, LOCAL_GLARE_DETECT);
00186       return 0;
00187    }
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                   ast_channel_masquerade(p->owner, p->chan->_bridge);
00268                   ast_set_flag(p, LOCAL_ALREADY_MASQED);
00269                }
00270                ast_mutex_unlock(&p->owner->lock);
00271             }
00272             ast_mutex_unlock(&(p->chan->_bridge)->lock);
00273          }
00274       }
00275    /* We only allow masquerading in one 'direction'... it's important to preserve the state
00276       (group variables, etc.) that live on p->chan->_bridge (and were put there by the dialplan)
00277       when the local channels go away.
00278    */
00279 #if 0
00280    } else if (!isoutbound && p->owner && p->owner->_bridge && p->chan && AST_LIST_EMPTY(&p->chan->readq)) {
00281       /* Masquerade bridged channel into chan */
00282       if (!ast_mutex_trylock(&(p->owner->_bridge)->lock)) {
00283          if (!p->owner->_bridge->_softhangup) {
00284             if (!ast_mutex_trylock(&p->chan->lock)) {
00285                if (!p->chan->_softhangup) {
00286                   ast_channel_masquerade(p->chan, p->owner->_bridge);
00287                   ast_set_flag(p, LOCAL_ALREADY_MASQED);
00288                }
00289                ast_mutex_unlock(&p->chan->lock);
00290             }
00291          }
00292          ast_mutex_unlock(&(p->owner->_bridge)->lock);
00293       }
00294 #endif
00295    }
00296 }
00297 
00298 static struct ast_frame  *local_read(struct ast_channel *ast)
00299 {
00300    return &ast_null_frame;
00301 }
00302 
00303 static int local_write(struct ast_channel *ast, struct ast_frame *f)
00304 {
00305    struct local_pvt *p = ast->tech_pvt;
00306    int res = -1;
00307    int isoutbound;
00308 
00309    if (!p)
00310       return -1;
00311 
00312    /* Just queue for delivery to the other side */
00313    ast_mutex_lock(&p->lock);
00314    isoutbound = IS_OUTBOUND(ast, p);
00315    if (f && (f->frametype == AST_FRAME_VOICE || f->frametype == AST_FRAME_VIDEO))
00316       check_bridge(p, isoutbound);
00317    if (!ast_test_flag(p, LOCAL_ALREADY_MASQED))
00318       res = local_queue_frame(p, isoutbound, f, ast, 1);
00319    else {
00320       if (option_debug)
00321          ast_log(LOG_DEBUG, "Not posting to queue since already masked on '%s'\n", ast->name);
00322       res = 0;
00323    }
00324    if (!res)
00325       ast_mutex_unlock(&p->lock);
00326    return res;
00327 }
00328 
00329 static int local_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
00330 {
00331    struct local_pvt *p = newchan->tech_pvt;
00332 
00333    if (!p)
00334       return -1;
00335 
00336    ast_mutex_lock(&p->lock);
00337 
00338    if ((p->owner != oldchan) && (p->chan != oldchan)) {
00339       ast_log(LOG_WARNING, "Old channel wasn't %p but was %p/%p\n", oldchan, p->owner, p->chan);
00340       ast_mutex_unlock(&p->lock);
00341       return -1;
00342    }
00343    if (p->owner == oldchan)
00344       p->owner = newchan;
00345    else
00346       p->chan = newchan;
00347    ast_mutex_unlock(&p->lock);
00348    return 0;
00349 }
00350 
00351 static int local_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen)
00352 {
00353    struct local_pvt *p = ast->tech_pvt;
00354    int res = 0;
00355    struct ast_frame f = { AST_FRAME_CONTROL, };
00356    int isoutbound;
00357 
00358    if (!p)
00359       return -1;
00360 
00361    /* If this is an MOH hold or unhold, do it on the Local channel versus real channel */
00362    if (condition == AST_CONTROL_HOLD) {
00363       ast_moh_start(ast, data, NULL);
00364    } else if (condition == AST_CONTROL_UNHOLD) {
00365       ast_moh_stop(ast);
00366    } else {
00367       /* Queue up a frame representing the indication as a control frame */
00368       ast_mutex_lock(&p->lock);
00369       isoutbound = IS_OUTBOUND(ast, p);
00370       f.subclass = condition;
00371       f.data = (void*)data;
00372       f.datalen = datalen;
00373       if (!(res = local_queue_frame(p, isoutbound, &f, ast, 1)))
00374          ast_mutex_unlock(&p->lock);
00375    }
00376 
00377    return res;
00378 }
00379 
00380 static int local_digit_begin(struct ast_channel *ast, char digit)
00381 {
00382    struct local_pvt *p = ast->tech_pvt;
00383    int res = -1;
00384    struct ast_frame f = { AST_FRAME_DTMF_BEGIN, };
00385    int isoutbound;
00386 
00387    if (!p)
00388       return -1;
00389 
00390    ast_mutex_lock(&p->lock);
00391    isoutbound = IS_OUTBOUND(ast, p);
00392    f.subclass = digit;
00393    if (!(res = local_queue_frame(p, isoutbound, &f, ast, 0)))
00394       ast_mutex_unlock(&p->lock);
00395 
00396    return res;
00397 }
00398 
00399 static int local_digit_end(struct ast_channel *ast, char digit, unsigned int duration)
00400 {
00401    struct local_pvt *p = ast->tech_pvt;
00402    int res = -1;
00403    struct ast_frame f = { AST_FRAME_DTMF_END, };
00404    int isoutbound;
00405 
00406    if (!p)
00407       return -1;
00408 
00409    ast_mutex_lock(&p->lock);
00410    isoutbound = IS_OUTBOUND(ast, p);
00411    f.subclass = digit;
00412    f.len = duration;
00413    if (!(res = local_queue_frame(p, isoutbound, &f, ast, 0)))
00414       ast_mutex_unlock(&p->lock);
00415 
00416    return res;
00417 }
00418 
00419 static int local_sendtext(struct ast_channel *ast, const char *text)
00420 {
00421    struct local_pvt *p = ast->tech_pvt;
00422    int res = -1;
00423    struct ast_frame f = { AST_FRAME_TEXT, };
00424    int isoutbound;
00425 
00426    if (!p)
00427       return -1;
00428 
00429    ast_mutex_lock(&p->lock);
00430    isoutbound = IS_OUTBOUND(ast, p);
00431    f.data = (char *) text;
00432    f.datalen = strlen(text) + 1;
00433    if (!(res = local_queue_frame(p, isoutbound, &f, ast, 0)))
00434       ast_mutex_unlock(&p->lock);
00435    return res;
00436 }
00437 
00438 static int local_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen)
00439 {
00440    struct local_pvt *p = ast->tech_pvt;
00441    int res = -1;
00442    struct ast_frame f = { AST_FRAME_HTML, };
00443    int isoutbound;
00444 
00445    if (!p)
00446       return -1;
00447    
00448    ast_mutex_lock(&p->lock);
00449    isoutbound = IS_OUTBOUND(ast, p);
00450    f.subclass = subclass;
00451    f.data = (char *)data;
00452    f.datalen = datalen;
00453    if (!(res = local_queue_frame(p, isoutbound, &f, ast, 0)))
00454       ast_mutex_unlock(&p->lock);
00455    return res;
00456 }
00457 
00458 /*! \brief Initiate new call, part of PBX interface 
00459  *    dest is the dial string */
00460 static int local_call(struct ast_channel *ast, char *dest, int timeout)
00461 {
00462    struct local_pvt *p = ast->tech_pvt;
00463    int res;
00464    struct ast_var_t *varptr = NULL, *new;
00465    size_t len, namelen;
00466 
00467    if (!p)
00468       return -1;
00469    
00470    ast_mutex_lock(&p->lock);
00471 
00472    /*
00473     * Note that cid_num and cid_name aren't passed in the ast_channel_alloc
00474     * call, so it's done here instead.
00475     */
00476    p->chan->cid.cid_dnid = ast_strdup(p->owner->cid.cid_dnid);
00477    p->chan->cid.cid_num = ast_strdup(p->owner->cid.cid_num);
00478    p->chan->cid.cid_name = ast_strdup(p->owner->cid.cid_name);
00479    p->chan->cid.cid_rdnis = ast_strdup(p->owner->cid.cid_rdnis);
00480    p->chan->cid.cid_ani = ast_strdup(p->owner->cid.cid_ani);
00481    p->chan->cid.cid_pres = p->owner->cid.cid_pres;
00482    p->chan->cid.cid_ani2 = p->owner->cid.cid_ani2;
00483    p->chan->cid.cid_ton = p->owner->cid.cid_ton;
00484    p->chan->cid.cid_tns = p->owner->cid.cid_tns;
00485    ast_string_field_set(p->chan, language, p->owner->language);
00486    ast_string_field_set(p->chan, accountcode, p->owner->accountcode);
00487    ast_cdr_update(p->chan);
00488    p->chan->cdrflags = p->owner->cdrflags;
00489 
00490    if (!ast_exists_extension(NULL, p->chan->context, p->chan->exten, 1, p->owner->cid.cid_num)) {
00491       ast_log(LOG_NOTICE, "No such extension/context %s@%s while calling Local channel\n", p->chan->exten, p->chan->context);
00492       ast_mutex_unlock(&p->lock);
00493       return -1;
00494    }
00495 
00496    /* copy the channel variables from the incoming channel to the outgoing channel */
00497    /* Note that due to certain assumptions, they MUST be in the same order */
00498    AST_LIST_TRAVERSE(&p->owner->varshead, varptr, entries) {
00499       namelen = strlen(varptr->name);
00500       len = sizeof(struct ast_var_t) + namelen + strlen(varptr->value) + 2;
00501       if ((new = ast_calloc(1, len))) {
00502          memcpy(new, varptr, len);
00503          new->value = &(new->name[0]) + namelen + 1;
00504          AST_LIST_INSERT_TAIL(&p->chan->varshead, new, entries);
00505       }
00506    }
00507    ast_channel_datastore_inherit(p->owner, p->chan);
00508 
00509    /* Start switch on sub channel */
00510    if (!(res = ast_pbx_start(p->chan)))
00511       ast_set_flag(p, LOCAL_LAUNCHED_PBX);
00512 
00513    ast_mutex_unlock(&p->lock);
00514    return res;
00515 }
00516 
00517 /*! \brief Hangup a call through the local proxy channel */
00518 static int local_hangup(struct ast_channel *ast)
00519 {
00520    struct local_pvt *p = ast->tech_pvt;
00521    int isoutbound;
00522    struct ast_frame f = { AST_FRAME_CONTROL, AST_CONTROL_HANGUP };
00523    struct ast_channel *ochan = NULL;
00524    int glaredetect = 0, res = 0;
00525 
00526    if (!p)
00527       return -1;
00528 
00529    while (ast_mutex_trylock(&p->lock)) {
00530       ast_channel_unlock(ast);
00531       usleep(1);
00532       ast_channel_lock(ast);
00533    }
00534 
00535    isoutbound = IS_OUTBOUND(ast, p);
00536    if (isoutbound) {
00537       const char *status = pbx_builtin_getvar_helper(p->chan, "DIALSTATUS");
00538       if ((status) && (p->owner)) {
00539          /* Deadlock avoidance */
00540          while (p->owner && ast_channel_trylock(p->owner)) {
00541             ast_mutex_unlock(&p->lock);
00542             if (ast) {
00543                ast_channel_unlock(ast);
00544             }
00545             usleep(1);
00546             if (ast) {
00547                ast_channel_lock(ast);
00548             }
00549             ast_mutex_lock(&p->lock);
00550          }
00551          if (p->owner) {
00552             pbx_builtin_setvar_helper(p->owner, "CHANLOCALSTATUS", status);
00553             ast_channel_unlock(p->owner);
00554          }
00555       }
00556       p->chan = NULL;
00557       ast_clear_flag(p, LOCAL_LAUNCHED_PBX);
00558       ast_module_user_remove(p->u_chan);
00559    } else {
00560       p->owner = NULL;
00561       ast_module_user_remove(p->u_owner);
00562       while (p->chan && ast_channel_trylock(p->chan)) {
00563          DEADLOCK_AVOIDANCE(&p->lock);
00564       }
00565       if (p->chan) {
00566          ast_queue_hangup(p->chan);
00567          ast_channel_unlock(p->chan);
00568       }
00569    }
00570    
00571    ast->tech_pvt = NULL;
00572    
00573    if (!p->owner && !p->chan) {
00574       /* Okay, done with the private part now, too. */
00575       glaredetect = ast_test_flag(p, LOCAL_GLARE_DETECT);
00576       /* If we have a queue holding, don't actually destroy p yet, but
00577          let local_queue do it. */
00578       if (glaredetect)
00579          ast_set_flag(p, LOCAL_CANCEL_QUEUE);
00580       ast_mutex_unlock(&p->lock);
00581       /* Remove from list */
00582       AST_LIST_LOCK(&locals);
00583       AST_LIST_REMOVE(&locals, p, list);
00584       AST_LIST_UNLOCK(&locals);
00585       /* Grab / release lock just in case */
00586       ast_mutex_lock(&p->lock);
00587       ast_mutex_unlock(&p->lock);
00588       /* And destroy */
00589       if (!glaredetect) {
00590          p = local_pvt_destroy(p);
00591       }
00592       return 0;
00593    }
00594    if (p->chan && !ast_test_flag(p, LOCAL_LAUNCHED_PBX))
00595       /* Need to actually hangup since there is no PBX */
00596       ochan = p->chan;
00597    else
00598       res = local_queue_frame(p, isoutbound, &f, NULL, 1);
00599    if (!res)
00600       ast_mutex_unlock(&p->lock);
00601    if (ochan)
00602       ast_hangup(ochan);
00603    return 0;
00604 }
00605 
00606 /*! \brief Create a call structure */
00607 static struct local_pvt *local_alloc(const char *data, int format)
00608 {
00609    struct local_pvt *tmp = NULL;
00610    char *c = NULL, *opts = NULL;
00611 
00612    if (!(tmp = ast_calloc(1, sizeof(*tmp))))
00613       return NULL;
00614 
00615    /* Initialize private structure information */
00616    ast_mutex_init(&tmp->lock);
00617    ast_copy_string(tmp->exten, data, sizeof(tmp->exten));
00618 
00619    /* Look for options */
00620    if ((opts = strchr(tmp->exten, '/'))) {
00621       *opts++ = '\0';
00622       if (strchr(opts, 'n'))
00623          ast_set_flag(tmp, LOCAL_NO_OPTIMIZATION);
00624    }
00625 
00626    /* Look for a context */
00627    if ((c = strchr(tmp->exten, '@')))
00628       *c++ = '\0';
00629 
00630    ast_copy_string(tmp->context, c ? c : "default", sizeof(tmp->context));
00631 
00632    tmp->reqformat = format;
00633 
00634 #if 0
00635    /* We can't do this check here, because we don't know the CallerID yet, and
00636     * the CallerID could potentially affect what step is actually taken (or
00637     * even if that step exists). */
00638    if (!ast_exists_extension(NULL, tmp->context, tmp->exten, 1, NULL)) {
00639       ast_log(LOG_NOTICE, "No such extension/context %s@%s creating local channel\n", tmp->exten, tmp->context);
00640       tmp = local_pvt_destroy(tmp);
00641    } else {
00642 #endif
00643       /* Add to list */
00644       AST_LIST_LOCK(&locals);
00645       AST_LIST_INSERT_HEAD(&locals, tmp, list);
00646       AST_LIST_UNLOCK(&locals);
00647 #if 0
00648    }
00649 #endif
00650    
00651    return tmp;
00652 }
00653 
00654 /*! \brief Start new local channel */
00655 static struct ast_channel *local_new(struct local_pvt *p, int state)
00656 {
00657    struct ast_channel *tmp = NULL, *tmp2 = NULL;
00658    int randnum = ast_random() & 0xffff, fmt = 0;
00659    const char *t;
00660    int ama;
00661 
00662    /* Allocate two new Asterisk channels */
00663    /* safe accountcode */
00664    if (p->owner && p->owner->accountcode)
00665       t = p->owner->accountcode;
00666    else
00667       t = "";
00668 
00669    if (p->owner)
00670       ama = p->owner->amaflags;
00671    else
00672       ama = 0;
00673    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)) 
00674          || !(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))) {
00675       if (tmp)
00676          ast_channel_free(tmp);
00677       if (tmp2)
00678          ast_channel_free(tmp2);
00679       ast_log(LOG_WARNING, "Unable to allocate channel structure(s)\n");
00680       return NULL;
00681    } 
00682 
00683    tmp2->tech = tmp->tech = &local_tech;
00684 
00685    tmp->nativeformats = p->reqformat;
00686    tmp2->nativeformats = p->reqformat;
00687 
00688    /* Determine our read/write format and set it on each channel */
00689    fmt = ast_best_codec(p->reqformat);
00690    tmp->writeformat = fmt;
00691    tmp2->writeformat = fmt;
00692    tmp->rawwriteformat = fmt;
00693    tmp2->rawwriteformat = fmt;
00694    tmp->readformat = fmt;
00695    tmp2->readformat = fmt;
00696    tmp->rawreadformat = fmt;
00697    tmp2->rawreadformat = fmt;
00698 
00699    tmp->tech_pvt = p;
00700    tmp2->tech_pvt = p;
00701 
00702    p->owner = tmp;
00703    p->chan = tmp2;
00704    p->u_owner = ast_module_user_add(p->owner);
00705    p->u_chan = ast_module_user_add(p->chan);
00706 
00707    ast_copy_string(tmp->context, p->context, sizeof(tmp->context));
00708    ast_copy_string(tmp2->context, p->context, sizeof(tmp2->context));
00709    ast_copy_string(tmp2->exten, p->exten, sizeof(tmp->exten));
00710    tmp->priority = 1;
00711    tmp2->priority = 1;
00712 
00713    return tmp;
00714 }
00715 
00716 /*! \brief Part of PBX interface */
00717 static struct ast_channel *local_request(const char *type, int format, void *data, int *cause)
00718 {
00719    struct local_pvt *p = NULL;
00720    struct ast_channel *chan = NULL;
00721 
00722    /* Allocate a new private structure and then Asterisk channel */
00723    if ((p = local_alloc(data, format))) {
00724       if (!(chan = local_new(p, AST_STATE_DOWN))) {
00725          AST_LIST_LOCK(&locals);
00726          AST_LIST_REMOVE(&locals, p, list);
00727          AST_LIST_UNLOCK(&locals);
00728          p = local_pvt_destroy(p);
00729       }
00730    }
00731 
00732    return chan;
00733 }
00734 
00735 /*! \brief CLI command "local show channels" */
00736 static int locals_show(int fd, int argc, char **argv)
00737 {
00738    struct local_pvt *p = NULL;
00739 
00740    if (argc != 3)
00741       return RESULT_SHOWUSAGE;
00742 
00743    AST_LIST_LOCK(&locals);
00744    if (!AST_LIST_EMPTY(&locals)) {
00745       AST_LIST_TRAVERSE(&locals, p, list) {
00746          ast_mutex_lock(&p->lock);
00747          ast_cli(fd, "%s -- %s@%s\n", p->owner ? p->owner->name : "<unowned>", p->exten, p->context);
00748          ast_mutex_unlock(&p->lock);
00749       }
00750    } else
00751       ast_cli(fd, "No local channels in use\n");
00752    AST_LIST_UNLOCK(&locals);
00753 
00754    return RESULT_SUCCESS;
00755 }
00756 
00757 static char show_locals_usage[] = 
00758 "Usage: local show channels\n"
00759 "       Provides summary information on active local proxy channels.\n";
00760 
00761 static struct ast_cli_entry cli_local[] = {
00762    { { "local", "show", "channels", NULL },
00763    locals_show, "List status of local channels",
00764    show_locals_usage },
00765 };
00766 
00767 /*! \brief Load module into PBX, register channel */
00768 static int load_module(void)
00769 {
00770    /* Make sure we can register our channel type */
00771    if (ast_channel_register(&local_tech)) {
00772       ast_log(LOG_ERROR, "Unable to register channel class 'Local'\n");
00773       return -1;
00774    }
00775    ast_cli_register_multiple(cli_local, sizeof(cli_local) / sizeof(struct ast_cli_entry));
00776    return 0;
00777 }
00778 
00779 /*! \brief Unload the local proxy channel from Asterisk */
00780 static int unload_module(void)
00781 {
00782    struct local_pvt *p = NULL;
00783 
00784    /* First, take us out of the channel loop */
00785    ast_cli_unregister_multiple(cli_local, sizeof(cli_local) / sizeof(struct ast_cli_entry));
00786    ast_channel_unregister(&local_tech);
00787    if (!AST_LIST_LOCK(&locals)) {
00788       /* Hangup all interfaces if they have an owner */
00789       AST_LIST_TRAVERSE(&locals, p, list) {
00790          if (p->owner)
00791             ast_softhangup(p->owner, AST_SOFTHANGUP_APPUNLOAD);
00792       }
00793       AST_LIST_UNLOCK(&locals);
00794    } else {
00795       ast_log(LOG_WARNING, "Unable to lock the monitor\n");
00796       return -1;
00797    }     
00798    return 0;
00799 }
00800 
00801 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Local Proxy Channel (Note: used internally by other modules)");

Generated on Sat Nov 1 06:28:25 2008 for Asterisk - the Open Source PBX by  doxygen 1.5.1