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

Generated on Mon Nov 24 15:34:15 2008 for Asterisk - the Open Source PBX by  doxygen 1.4.7