Sat Mar 24 22:55:14 2007

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  * \brief Local Proxy Channel
00022  * 
00023  * \ingroup channel_drivers
00024  */
00025 
00026 #include <stdio.h>
00027 #include <string.h>
00028 #include <unistd.h>
00029 #include <sys/socket.h>
00030 #include <errno.h>
00031 #include <stdlib.h>
00032 #include <fcntl.h>
00033 #include <netdb.h>
00034 #include <netinet/in.h>
00035 #include <arpa/inet.h>
00036 #include <sys/signal.h>
00037 
00038 #include "asterisk.h"
00039 
00040 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 42402 $")
00041 
00042 #include "asterisk/lock.h"
00043 #include "asterisk/channel.h"
00044 #include "asterisk/config.h"
00045 #include "asterisk/logger.h"
00046 #include "asterisk/module.h"
00047 #include "asterisk/pbx.h"
00048 #include "asterisk/options.h"
00049 #include "asterisk/lock.h"
00050 #include "asterisk/sched.h"
00051 #include "asterisk/io.h"
00052 #include "asterisk/rtp.h"
00053 #include "asterisk/acl.h"
00054 #include "asterisk/callerid.h"
00055 #include "asterisk/file.h"
00056 #include "asterisk/cli.h"
00057 #include "asterisk/app.h"
00058 #include "asterisk/musiconhold.h"
00059 #include "asterisk/manager.h"
00060 
00061 static const char desc[] = "Local Proxy Channel";
00062 static const char type[] = "Local";
00063 static const char tdesc[] = "Local Proxy Channel Driver";
00064 
00065 static int usecnt =0;
00066 AST_MUTEX_DEFINE_STATIC(usecnt_lock);
00067 
00068 #define IS_OUTBOUND(a,b) (a == b->chan ? 1 : 0)
00069 
00070 /* Protect the interface list (of sip_pvt's) */
00071 AST_MUTEX_DEFINE_STATIC(locallock);
00072 
00073 static struct ast_channel *local_request(const char *type, int format, void *data, int *cause);
00074 static int local_digit(struct ast_channel *ast, char digit);
00075 static int local_call(struct ast_channel *ast, char *dest, int timeout);
00076 static int local_hangup(struct ast_channel *ast);
00077 static int local_answer(struct ast_channel *ast);
00078 static struct ast_frame *local_read(struct ast_channel *ast);
00079 static int local_write(struct ast_channel *ast, struct ast_frame *f);
00080 static int local_indicate(struct ast_channel *ast, int condition);
00081 static int local_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
00082 static int local_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen);
00083 
00084 /* PBX interface structure for channel registration */
00085 static const struct ast_channel_tech local_tech = {
00086    .type = type,
00087    .description = tdesc,
00088    .capabilities = -1,
00089    .requester = local_request,
00090    .send_digit = local_digit,
00091    .call = local_call,
00092    .hangup = local_hangup,
00093    .answer = local_answer,
00094    .read = local_read,
00095    .write = local_write,
00096    .exception = local_read,
00097    .indicate = local_indicate,
00098    .fixup = local_fixup,
00099    .send_html = local_sendhtml,
00100 };
00101 
00102 static struct local_pvt {
00103    ast_mutex_t lock;       /* Channel private lock */
00104    char context[AST_MAX_CONTEXT];      /* Context to call */
00105    char exten[AST_MAX_EXTENSION];      /* Extension to call */
00106    int reqformat;          /* Requested format */
00107    int glaredetect;        /* Detect glare on hangup */
00108    int cancelqueue;        /* Cancel queue */
00109    int alreadymasqed;         /* Already masqueraded */
00110    int launchedpbx;        /* Did we launch the PBX */
00111    int nooptimization;        /* Don't leave masq state */
00112    struct ast_channel *owner;    /* Master Channel */
00113    struct ast_channel *chan;     /* Outbound channel */
00114    struct local_pvt *next;       /* Next entity */
00115 } *locals = NULL;
00116 
00117 static int local_queue_frame(struct local_pvt *p, int isoutbound, struct ast_frame *f, struct ast_channel *us)
00118 {
00119    struct ast_channel *other;
00120 retrylock:     
00121    /* Recalculate outbound channel */
00122    if (isoutbound) {
00123       other = p->owner;
00124    } else {
00125       other = p->chan;
00126    }
00127    /* Set glare detection */
00128    p->glaredetect = 1;
00129    if (p->cancelqueue) {
00130       /* We had a glare on the hangup.  Forget all this business,
00131       return and destroy p.  */
00132       ast_mutex_unlock(&p->lock);
00133       ast_mutex_destroy(&p->lock);
00134       free(p);
00135       return -1;
00136    }
00137    if (!other) {
00138       p->glaredetect = 0;
00139       return 0;
00140    }
00141    if (ast_mutex_trylock(&other->lock)) {
00142       /* Failed to lock.  Release main lock and try again */
00143       ast_mutex_unlock(&p->lock);
00144       if (us) {
00145          if (ast_mutex_unlock(&us->lock)) {
00146             ast_log(LOG_WARNING, "%s wasn't locked while sending %d/%d\n",
00147                us->name, f->frametype, f->subclass);
00148             us = NULL;
00149          }
00150       }
00151       /* Wait just a bit */
00152       usleep(1);
00153       /* Only we can destroy ourselves, so we can't disappear here */
00154       if (us)
00155          ast_mutex_lock(&us->lock);
00156       ast_mutex_lock(&p->lock);
00157       goto retrylock;
00158    }
00159    ast_queue_frame(other, f);
00160    ast_mutex_unlock(&other->lock);
00161    p->glaredetect = 0;
00162    return 0;
00163 }
00164 
00165 static int local_answer(struct ast_channel *ast)
00166 {
00167    struct local_pvt *p = ast->tech_pvt;
00168    int isoutbound;
00169    int res = -1;
00170 
00171    ast_mutex_lock(&p->lock);
00172    isoutbound = IS_OUTBOUND(ast, p);
00173    if (isoutbound) {
00174       /* Pass along answer since somebody answered us */
00175       struct ast_frame answer = { AST_FRAME_CONTROL, AST_CONTROL_ANSWER };
00176       res = local_queue_frame(p, isoutbound, &answer, ast);
00177    } else
00178       ast_log(LOG_WARNING, "Huh?  Local is being asked to answer?\n");
00179    ast_mutex_unlock(&p->lock);
00180    return res;
00181 }
00182 
00183 static void check_bridge(struct local_pvt *p, int isoutbound)
00184 {
00185    if (p->alreadymasqed || p->nooptimization)
00186       return;
00187    if (!p->chan || !p->owner)
00188       return;
00189 
00190    /* only do the masquerade if we are being called on the outbound channel,
00191       if it has been bridged to another channel and if there are no pending
00192       frames on the owner channel (because they would be transferred to the
00193       outbound channel during the masquerade)
00194    */
00195    if (isoutbound && p->chan->_bridge /* Not ast_bridged_channel!  Only go one step! */ && !p->owner->readq) {
00196       /* Masquerade bridged channel into owner */
00197       /* Lock everything we need, one by one, and give up if
00198          we can't get everything.  Remember, we'll get another
00199          chance in just a little bit */
00200       if (!ast_mutex_trylock(&(p->chan->_bridge)->lock)) {
00201          if (!p->chan->_bridge->_softhangup) {
00202             if (!ast_mutex_trylock(&p->owner->lock)) {
00203                if (!p->owner->_softhangup) {
00204                   ast_channel_masquerade(p->owner, p->chan->_bridge);
00205                   p->alreadymasqed = 1;
00206                }
00207                ast_mutex_unlock(&p->owner->lock);
00208             }
00209             ast_mutex_unlock(&(p->chan->_bridge)->lock);
00210          }
00211       }
00212    /* We only allow masquerading in one 'direction'... it's important to preserve the state
00213       (group variables, etc.) that live on p->chan->_bridge (and were put there by the dialplan)
00214       when the local channels go away.
00215    */
00216 #if 0
00217    } else if (!isoutbound && p->owner && p->owner->_bridge && p->chan && !p->chan->readq) {
00218       /* Masquerade bridged channel into chan */
00219       if (!ast_mutex_trylock(&(p->owner->_bridge)->lock)) {
00220          if (!p->owner->_bridge->_softhangup) {
00221             if (!ast_mutex_trylock(&p->chan->lock)) {
00222                if (!p->chan->_softhangup) {
00223                   ast_channel_masquerade(p->chan, p->owner->_bridge);
00224                   p->alreadymasqed = 1;
00225                }
00226                ast_mutex_unlock(&p->chan->lock);
00227             }
00228          }
00229          ast_mutex_unlock(&(p->owner->_bridge)->lock);
00230       }
00231 #endif
00232    }
00233 }
00234 
00235 static struct ast_frame  *local_read(struct ast_channel *ast)
00236 {
00237    static struct ast_frame null = { AST_FRAME_NULL, };
00238 
00239    return &null;
00240 }
00241 
00242 static int local_write(struct ast_channel *ast, struct ast_frame *f)
00243 {
00244    struct local_pvt *p = ast->tech_pvt;
00245    int res = -1;
00246    int isoutbound;
00247 
00248    /* Just queue for delivery to the other side */
00249    ast_mutex_lock(&p->lock);
00250    isoutbound = IS_OUTBOUND(ast, p);
00251    if (f && (f->frametype == AST_FRAME_VOICE)) 
00252       check_bridge(p, isoutbound);
00253    if (!p->alreadymasqed)
00254       res = local_queue_frame(p, isoutbound, f, ast);
00255    else {
00256       ast_log(LOG_DEBUG, "Not posting to queue since already masked on '%s'\n", ast->name);
00257       res = 0;
00258    }
00259    ast_mutex_unlock(&p->lock);
00260    return res;
00261 }
00262 
00263 static int local_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
00264 {
00265    struct local_pvt *p = newchan->tech_pvt;
00266    ast_mutex_lock(&p->lock);
00267 
00268    if ((p->owner != oldchan) && (p->chan != oldchan)) {
00269       ast_log(LOG_WARNING, "Old channel wasn't %p but was %p/%p\n", oldchan, p->owner, p->chan);
00270       ast_mutex_unlock(&p->lock);
00271       return -1;
00272    }
00273    if (p->owner == oldchan)
00274       p->owner = newchan;
00275    else
00276       p->chan = newchan;
00277    ast_mutex_unlock(&p->lock);
00278    return 0;
00279 }
00280 
00281 static int local_indicate(struct ast_channel *ast, int condition)
00282 {
00283    struct local_pvt *p = ast->tech_pvt;
00284    int res = -1;
00285    struct ast_frame f = { AST_FRAME_CONTROL, };
00286    int isoutbound;
00287 
00288    /* Queue up a frame representing the indication as a control frame */
00289    ast_mutex_lock(&p->lock);
00290    isoutbound = IS_OUTBOUND(ast, p);
00291    f.subclass = condition;
00292    res = local_queue_frame(p, isoutbound, &f, ast);
00293    ast_mutex_unlock(&p->lock);
00294    return res;
00295 }
00296 
00297 static int local_digit(struct ast_channel *ast, char digit)
00298 {
00299    struct local_pvt *p = ast->tech_pvt;
00300    int res = -1;
00301    struct ast_frame f = { AST_FRAME_DTMF, };
00302    int isoutbound;
00303 
00304    ast_mutex_lock(&p->lock);
00305    isoutbound = IS_OUTBOUND(ast, p);
00306    f.subclass = digit;
00307    res = local_queue_frame(p, isoutbound, &f, ast);
00308    ast_mutex_unlock(&p->lock);
00309    return res;
00310 }
00311 
00312 static int local_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen)
00313 {
00314    struct local_pvt *p = ast->tech_pvt;
00315    int res = -1;
00316    struct ast_frame f = { AST_FRAME_HTML, };
00317    int isoutbound;
00318 
00319    ast_mutex_lock(&p->lock);
00320    isoutbound = IS_OUTBOUND(ast, p);
00321    f.subclass = subclass;
00322    f.data = (char *)data;
00323    f.datalen = datalen;
00324    res = local_queue_frame(p, isoutbound, &f, ast);
00325    ast_mutex_unlock(&p->lock);
00326    return res;
00327 }
00328 
00329 /*--- local_call: Initiate new call, part of PBX interface */
00330 /*    dest is the dial string */
00331 static int local_call(struct ast_channel *ast, char *dest, int timeout)
00332 {
00333    struct local_pvt *p = ast->tech_pvt;
00334    int res;
00335    struct ast_var_t *varptr = NULL, *new;
00336    size_t len, namelen;
00337    
00338    ast_mutex_lock(&p->lock);
00339 
00340    ast_set_callerid(p->chan,
00341       p->owner->cid.cid_num, p->owner->cid.cid_name,
00342       p->owner->cid.cid_ani);
00343 
00344    if (p->owner->cid.cid_rdnis)
00345       p->chan->cid.cid_rdnis = strdup(p->owner->cid.cid_rdnis);
00346    else
00347       p->chan->cid.cid_rdnis = NULL;
00348 
00349    p->chan->cid.cid_pres = p->owner->cid.cid_pres;
00350 
00351    strncpy(p->chan->language, p->owner->language, sizeof(p->chan->language) - 1);
00352    strncpy(p->chan->accountcode, p->owner->accountcode, sizeof(p->chan->accountcode) - 1);
00353    p->chan->cdrflags = p->owner->cdrflags;
00354 
00355    /* copy the channel variables from the incoming channel to the outgoing channel */
00356    /* Note that due to certain assumptions, they MUST be in the same order */
00357    AST_LIST_TRAVERSE(&p->owner->varshead, varptr, entries) {
00358       namelen = strlen(varptr->name);
00359       len = sizeof(struct ast_var_t) + namelen + strlen(varptr->value) + 2;
00360       new = malloc(len);
00361       if (new) {
00362          memcpy(new, varptr, len);
00363          new->value = &(new->name[0]) + namelen + 1;
00364          AST_LIST_INSERT_TAIL(&p->chan->varshead, new, entries);
00365       } else {
00366          ast_log(LOG_ERROR, "Out of memory!\n");
00367       }
00368    }
00369 
00370    p->launchedpbx = 1;
00371 
00372    /* Start switch on sub channel */
00373    res = ast_pbx_start(p->chan);
00374    ast_mutex_unlock(&p->lock);
00375    return res;
00376 }
00377 
00378 #if 0
00379 static void local_destroy(struct local_pvt *p)
00380 {
00381    struct local_pvt *cur, *prev = NULL;
00382    ast_mutex_lock(&locallock);
00383    cur = locals;
00384    while(cur) {
00385       if (cur == p) {
00386          if (prev)
00387             prev->next = cur->next;
00388          else
00389             locals = cur->next;
00390          ast_mutex_destroy(cur);
00391          free(cur);
00392          break;
00393       }
00394       prev = cur;
00395       cur = cur->next;
00396    }
00397    ast_mutex_unlock(&locallock);
00398    if (!cur)
00399       ast_log(LOG_WARNING, "Unable ot find local '%s@%s' in local list\n", p->exten, p->context);
00400 }
00401 #endif
00402 
00403 /*--- local_hangup: Hangup a call through the local proxy channel */
00404 static int local_hangup(struct ast_channel *ast)
00405 {
00406    struct local_pvt *p = ast->tech_pvt;
00407    int isoutbound;
00408    struct ast_frame f = { AST_FRAME_CONTROL, AST_CONTROL_HANGUP };
00409    struct local_pvt *cur, *prev=NULL;
00410    struct ast_channel *ochan = NULL;
00411    int glaredetect;
00412 
00413    ast_mutex_lock(&p->lock);
00414    isoutbound = IS_OUTBOUND(ast, p);
00415    if (isoutbound) {
00416       p->chan = NULL;
00417       p->launchedpbx = 0;
00418    } else
00419       p->owner = NULL;
00420    ast->tech_pvt = NULL;
00421    
00422    ast_mutex_lock(&usecnt_lock);
00423    usecnt--;
00424    ast_mutex_unlock(&usecnt_lock);
00425    
00426    if (!p->owner && !p->chan) {
00427       /* Okay, done with the private part now, too. */
00428       glaredetect = p->glaredetect;
00429       /* If we have a queue holding, don't actually destroy p yet, but
00430          let local_queue do it. */
00431       if (p->glaredetect)
00432          p->cancelqueue = 1;
00433       ast_mutex_unlock(&p->lock);
00434       /* Remove from list */
00435       ast_mutex_lock(&locallock);
00436       cur = locals;
00437       while(cur) {
00438          if (cur == p) {
00439             if (prev)
00440                prev->next = cur->next;
00441             else
00442                locals = cur->next;
00443             break;
00444          }
00445          prev = cur;
00446          cur = cur->next;
00447       }
00448       ast_mutex_unlock(&locallock);
00449       /* Grab / release lock just in case */
00450       ast_mutex_lock(&p->lock);
00451       ast_mutex_unlock(&p->lock);
00452       /* And destroy */
00453       if (!glaredetect) {
00454          ast_mutex_destroy(&p->lock);
00455          free(p);
00456       }
00457       return 0;
00458    }
00459    if (p->chan && !p->launchedpbx)
00460       /* Need to actually hangup since there is no PBX */
00461       ochan = p->chan;
00462    else
00463       local_queue_frame(p, isoutbound, &f, NULL);
00464    ast_mutex_unlock(&p->lock);
00465    if (ochan)
00466       ast_hangup(ochan);
00467    return 0;
00468 }
00469 
00470 /*--- local_alloc: Create a call structure */
00471 static struct local_pvt *local_alloc(char *data, int format)
00472 {
00473    struct local_pvt *tmp;
00474    char *c;
00475    char *opts;
00476 
00477    tmp = malloc(sizeof(struct local_pvt));
00478    if (tmp) {
00479       memset(tmp, 0, sizeof(struct local_pvt));
00480       ast_mutex_init(&tmp->lock);
00481       strncpy(tmp->exten, data, sizeof(tmp->exten) - 1);
00482       opts = strchr(tmp->exten, '/');
00483       if (opts) {
00484          *opts='\0';
00485          opts++;
00486          if (strchr(opts, 'n'))
00487             tmp->nooptimization = 1;
00488       }
00489       c = strchr(tmp->exten, '@');
00490       if (c) {
00491          *c = '\0';
00492          c++;
00493          strncpy(tmp->context, c, sizeof(tmp->context) - 1);
00494       } else
00495          strncpy(tmp->context, "default", sizeof(tmp->context) - 1);
00496       tmp->reqformat = format;
00497       if (!ast_exists_extension(NULL, tmp->context, tmp->exten, 1, NULL)) {
00498          ast_log(LOG_NOTICE, "No such extension/context %s@%s creating local channel\n", tmp->exten, tmp->context);
00499          ast_mutex_destroy(&tmp->lock);
00500          free(tmp);
00501          tmp = NULL;
00502       } else {
00503          /* Add to list */
00504          ast_mutex_lock(&locallock);
00505          tmp->next = locals;
00506          locals = tmp;
00507          ast_mutex_unlock(&locallock);
00508       }
00509       
00510    }
00511    return tmp;
00512 }
00513 
00514 /*--- local_new: Start new local channel */
00515 static struct ast_channel *local_new(struct local_pvt *p, int state)
00516 {
00517    struct ast_channel *tmp, *tmp2;
00518    int randnum = rand() & 0xffff, fmt = 0;
00519 
00520    tmp = ast_channel_alloc(1);
00521    tmp2 = ast_channel_alloc(1);
00522    if (!tmp || !tmp2) {
00523       if (tmp)
00524          ast_channel_free(tmp);
00525       if (tmp2)
00526          ast_channel_free(tmp2);
00527       ast_log(LOG_WARNING, "Unable to allocate channel structure(s)\n");
00528       return NULL;
00529    } 
00530 
00531    tmp2->tech = tmp->tech = &local_tech;
00532    tmp->nativeformats = p->reqformat;
00533    tmp2->nativeformats = p->reqformat;
00534    snprintf(tmp->name, sizeof(tmp->name), "Local/%s@%s-%04x,1", p->exten, p->context, randnum);
00535    snprintf(tmp2->name, sizeof(tmp2->name), "Local/%s@%s-%04x,2", p->exten, p->context, randnum);
00536    tmp->type = type;
00537    tmp2->type = type;
00538    ast_setstate(tmp, state);
00539    ast_setstate(tmp2, AST_STATE_RING);
00540    fmt = ast_best_codec(p->reqformat);
00541    tmp->writeformat = fmt;
00542    tmp2->writeformat = fmt;
00543    tmp->rawwriteformat = fmt;
00544    tmp2->rawwriteformat = fmt;
00545    tmp->readformat = fmt;
00546    tmp2->readformat = fmt;
00547    tmp->rawreadformat = fmt;
00548    tmp2->rawreadformat = fmt;
00549    tmp->tech_pvt = p;
00550    tmp2->tech_pvt = p;
00551    p->owner = tmp;
00552    p->chan = tmp2;
00553    ast_mutex_lock(&usecnt_lock);
00554    usecnt++;
00555    usecnt++;
00556    ast_mutex_unlock(&usecnt_lock);
00557    ast_update_use_count();
00558    ast_copy_string(tmp->context, p->context, sizeof(tmp->context));
00559    ast_copy_string(tmp2->context, p->context, sizeof(tmp2->context));
00560    ast_copy_string(tmp2->exten, p->exten, sizeof(tmp->exten));
00561    tmp->priority = 1;
00562    tmp2->priority = 1;
00563 
00564    return tmp;
00565 }
00566 
00567 
00568 /*--- local_request: Part of PBX interface */
00569 static struct ast_channel *local_request(const char *type, int format, void *data, int *cause)
00570 {
00571    struct local_pvt *p;
00572    struct ast_channel *chan = NULL;
00573 
00574    p = local_alloc(data, format);
00575    if (p)
00576       chan = local_new(p, AST_STATE_DOWN);
00577    return chan;
00578 }
00579 
00580 /*--- locals_show: CLI command "local show channels" */
00581 static int locals_show(int fd, int argc, char **argv)
00582 {
00583    struct local_pvt *p;
00584 
00585    if (argc != 3)
00586       return RESULT_SHOWUSAGE;
00587    ast_mutex_lock(&locallock);
00588    p = locals;
00589    while(p) {
00590       ast_mutex_lock(&p->lock);
00591       ast_cli(fd, "%s -- %s@%s\n", p->owner ? p->owner->name : "<unowned>", p->exten, p->context);
00592       ast_mutex_unlock(&p->lock);
00593       p = p->next;
00594    }
00595    if (!locals)
00596       ast_cli(fd, "No local channels in use\n");
00597    ast_mutex_unlock(&locallock);
00598    return RESULT_SUCCESS;
00599 }
00600 
00601 static char show_locals_usage[] = 
00602 "Usage: local show channels\n"
00603 "       Provides summary information on active local proxy channels.\n";
00604 
00605 static struct ast_cli_entry cli_show_locals = {
00606    { "local", "show", "channels", NULL }, locals_show, 
00607    "Show status of local channels", show_locals_usage, NULL };
00608 
00609 /*--- load_module: Load module into PBX, register channel */
00610 int load_module()
00611 {
00612    /* Make sure we can register our channel type */
00613    if (ast_channel_register(&local_tech)) {
00614       ast_log(LOG_ERROR, "Unable to register channel class %s\n", type);
00615       return -1;
00616    }
00617    ast_cli_register(&cli_show_locals);
00618    return 0;
00619 }
00620 
00621 /*--- reload: Reload module */
00622 int reload()
00623 {
00624    return 0;
00625 }
00626 
00627 /*--- unload_module: Unload the local proxy channel from Asterisk */
00628 int unload_module()
00629 {
00630    struct local_pvt *p;
00631 
00632    /* First, take us out of the channel loop */
00633    ast_cli_unregister(&cli_show_locals);
00634    ast_channel_unregister(&local_tech);
00635    if (!ast_mutex_lock(&locallock)) {
00636       /* Hangup all interfaces if they have an owner */
00637       p = locals;
00638       while(p) {
00639          if (p->owner)
00640             ast_softhangup(p->owner, AST_SOFTHANGUP_APPUNLOAD);
00641          p = p->next;
00642       }
00643       locals = NULL;
00644       ast_mutex_unlock(&locallock);
00645    } else {
00646       ast_log(LOG_WARNING, "Unable to lock the monitor\n");
00647       return -1;
00648    }     
00649    return 0;
00650 }
00651 
00652 int usecount()
00653 {
00654    return usecnt;
00655 }
00656 
00657 char *key()
00658 {
00659    return ASTERISK_GPL_KEY;
00660 }
00661 
00662 char *description()
00663 {
00664    return (char *) desc;
00665 }
00666 

Generated on Sat Mar 24 22:55:14 2007 for Asterisk - the Open Source PBX by  doxygen 1.4.7