Fri May 26 01:45:30 2006

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: 7270 $")
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    if (isoutbound&& p->chan->_bridge /* Not ast_bridged_channel!  Only go one step! */ && !p->owner->readq) {
00190       /* Masquerade bridged channel into owner */
00191       /* Lock everything we need, one by one, and give up if
00192          we can't get everything.  Remember, we'll get another
00193          chance in just a little bit */
00194       if (!ast_mutex_trylock(&(p->chan->_bridge)->lock)) {
00195          if (!p->chan->_bridge->_softhangup) {
00196             if (!ast_mutex_trylock(&p->owner->lock)) {
00197                if (!p->owner->_softhangup) {
00198                   ast_channel_masquerade(p->owner, p->chan->_bridge);
00199                   p->alreadymasqed = 1;
00200                }
00201                ast_mutex_unlock(&p->owner->lock);
00202             }
00203             ast_mutex_unlock(&(p->chan->_bridge)->lock);
00204          }
00205       }
00206    } else if (!isoutbound && p->owner && p->owner->_bridge && p->chan && !p->chan->readq) {
00207       /* Masquerade bridged channel into chan */
00208       if (!ast_mutex_trylock(&(p->owner->_bridge)->lock)) {
00209          if (!p->owner->_bridge->_softhangup) {
00210             if (!ast_mutex_trylock(&p->chan->lock)) {
00211                if (!p->chan->_softhangup) {
00212                   ast_channel_masquerade(p->chan, p->owner->_bridge);
00213                   p->alreadymasqed = 1;
00214                }
00215                ast_mutex_unlock(&p->chan->lock);
00216             }
00217          }
00218          ast_mutex_unlock(&(p->owner->_bridge)->lock);
00219       }
00220    }
00221 }
00222 
00223 static struct ast_frame  *local_read(struct ast_channel *ast)
00224 {
00225    static struct ast_frame null = { AST_FRAME_NULL, };
00226 
00227    return &null;
00228 }
00229 
00230 static int local_write(struct ast_channel *ast, struct ast_frame *f)
00231 {
00232    struct local_pvt *p = ast->tech_pvt;
00233    int res = -1;
00234    int isoutbound;
00235 
00236    /* Just queue for delivery to the other side */
00237    ast_mutex_lock(&p->lock);
00238    isoutbound = IS_OUTBOUND(ast, p);
00239    if (f && (f->frametype == AST_FRAME_VOICE)) 
00240       check_bridge(p, isoutbound);
00241    if (!p->alreadymasqed)
00242       res = local_queue_frame(p, isoutbound, f, ast);
00243    else {
00244       ast_log(LOG_DEBUG, "Not posting to queue since already masked on '%s'\n", ast->name);
00245       res = 0;
00246    }
00247    ast_mutex_unlock(&p->lock);
00248    return res;
00249 }
00250 
00251 static int local_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
00252 {
00253    struct local_pvt *p = newchan->tech_pvt;
00254    ast_mutex_lock(&p->lock);
00255 
00256    if ((p->owner != oldchan) && (p->chan != oldchan)) {
00257       ast_log(LOG_WARNING, "Old channel wasn't %p but was %p/%p\n", oldchan, p->owner, p->chan);
00258       ast_mutex_unlock(&p->lock);
00259       return -1;
00260    }
00261    if (p->owner == oldchan)
00262       p->owner = newchan;
00263    else
00264       p->chan = newchan;
00265    ast_mutex_unlock(&p->lock);
00266    return 0;
00267 }
00268 
00269 static int local_indicate(struct ast_channel *ast, int condition)
00270 {
00271    struct local_pvt *p = ast->tech_pvt;
00272    int res = -1;
00273    struct ast_frame f = { AST_FRAME_CONTROL, };
00274    int isoutbound;
00275 
00276    /* Queue up a frame representing the indication as a control frame */
00277    ast_mutex_lock(&p->lock);
00278    isoutbound = IS_OUTBOUND(ast, p);
00279    f.subclass = condition;
00280    res = local_queue_frame(p, isoutbound, &f, ast);
00281    ast_mutex_unlock(&p->lock);
00282    return res;
00283 }
00284 
00285 static int local_digit(struct ast_channel *ast, char digit)
00286 {
00287    struct local_pvt *p = ast->tech_pvt;
00288    int res = -1;
00289    struct ast_frame f = { AST_FRAME_DTMF, };
00290    int isoutbound;
00291 
00292    ast_mutex_lock(&p->lock);
00293    isoutbound = IS_OUTBOUND(ast, p);
00294    f.subclass = digit;
00295    res = local_queue_frame(p, isoutbound, &f, ast);
00296    ast_mutex_unlock(&p->lock);
00297    return res;
00298 }
00299 
00300 static int local_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen)
00301 {
00302    struct local_pvt *p = ast->tech_pvt;
00303    int res = -1;
00304    struct ast_frame f = { AST_FRAME_HTML, };
00305    int isoutbound;
00306 
00307    ast_mutex_lock(&p->lock);
00308    isoutbound = IS_OUTBOUND(ast, p);
00309    f.subclass = subclass;
00310    f.data = (char *)data;
00311    f.datalen = datalen;
00312    res = local_queue_frame(p, isoutbound, &f, ast);
00313    ast_mutex_unlock(&p->lock);
00314    return res;
00315 }
00316 
00317 /*--- local_call: Initiate new call, part of PBX interface */
00318 /*    dest is the dial string */
00319 static int local_call(struct ast_channel *ast, char *dest, int timeout)
00320 {
00321    struct local_pvt *p = ast->tech_pvt;
00322    int res;
00323    struct ast_var_t *varptr = NULL, *new;
00324    size_t len, namelen;
00325    
00326    ast_mutex_lock(&p->lock);
00327    if (p->owner->cid.cid_num)
00328       p->chan->cid.cid_num = strdup(p->owner->cid.cid_num);
00329    else 
00330       p->chan->cid.cid_num = NULL;
00331 
00332    if (p->owner->cid.cid_name)
00333       p->chan->cid.cid_name = strdup(p->owner->cid.cid_name);
00334    else 
00335       p->chan->cid.cid_name = NULL;
00336 
00337    if (p->owner->cid.cid_rdnis)
00338       p->chan->cid.cid_rdnis = strdup(p->owner->cid.cid_rdnis);
00339    else
00340       p->chan->cid.cid_rdnis = NULL;
00341 
00342    if (p->owner->cid.cid_ani)
00343       p->chan->cid.cid_ani = strdup(p->owner->cid.cid_ani);
00344    else
00345       p->chan->cid.cid_ani = NULL;
00346 
00347    strncpy(p->chan->language, p->owner->language, sizeof(p->chan->language) - 1);
00348    strncpy(p->chan->accountcode, p->owner->accountcode, sizeof(p->chan->accountcode) - 1);
00349    p->chan->cdrflags = p->owner->cdrflags;
00350 
00351    /* copy the channel variables from the incoming channel to the outgoing channel */
00352    /* Note that due to certain assumptions, they MUST be in the same order */
00353    AST_LIST_TRAVERSE(&p->owner->varshead, varptr, entries) {
00354       namelen = strlen(varptr->name);
00355       len = sizeof(struct ast_var_t) + namelen + strlen(varptr->value) + 2;
00356       new = malloc(len);
00357       if (new) {
00358          memcpy(new, varptr, len);
00359          new->value = &(new->name[0]) + namelen + 1;
00360          AST_LIST_INSERT_TAIL(&p->chan->varshead, new, entries);
00361       } else {
00362          ast_log(LOG_ERROR, "Out of memory!\n");
00363       }
00364    }
00365 
00366    p->launchedpbx = 1;
00367 
00368    /* Start switch on sub channel */
00369    res = ast_pbx_start(p->chan);
00370    ast_mutex_unlock(&p->lock);
00371    return res;
00372 }
00373 
00374 #if 0
00375 static void local_destroy(struct local_pvt *p)
00376 {
00377    struct local_pvt *cur, *prev = NULL;
00378    ast_mutex_lock(&locallock);
00379    cur = locals;
00380    while(cur) {
00381       if (cur == p) {
00382          if (prev)
00383             prev->next = cur->next;
00384          else
00385             locals = cur->next;
00386          ast_mutex_destroy(cur);
00387          free(cur);
00388          break;
00389       }
00390       prev = cur;
00391       cur = cur->next;
00392    }
00393    ast_mutex_unlock(&locallock);
00394    if (!cur)
00395       ast_log(LOG_WARNING, "Unable ot find local '%s@%s' in local list\n", p->exten, p->context);
00396 }
00397 #endif
00398 
00399 /*--- local_hangup: Hangup a call through the local proxy channel */
00400 static int local_hangup(struct ast_channel *ast)
00401 {
00402    struct local_pvt *p = ast->tech_pvt;
00403    int isoutbound;
00404    struct ast_frame f = { AST_FRAME_CONTROL, AST_CONTROL_HANGUP };
00405    struct local_pvt *cur, *prev=NULL;
00406    struct ast_channel *ochan = NULL;
00407    int glaredetect;
00408 
00409    ast_mutex_lock(&p->lock);
00410    isoutbound = IS_OUTBOUND(ast, p);
00411    if (isoutbound) {
00412       p->chan = NULL;
00413       p->launchedpbx = 0;
00414    } else
00415       p->owner = NULL;
00416    ast->tech_pvt = NULL;
00417    
00418    ast_mutex_lock(&usecnt_lock);
00419    usecnt--;
00420    ast_mutex_unlock(&usecnt_lock);
00421    
00422    if (!p->owner && !p->chan) {
00423       /* Okay, done with the private part now, too. */
00424       glaredetect = p->glaredetect;
00425       /* If we have a queue holding, don't actually destroy p yet, but
00426          let local_queue do it. */
00427       if (p->glaredetect)
00428          p->cancelqueue = 1;
00429       ast_mutex_unlock(&p->lock);
00430       /* Remove from list */
00431       ast_mutex_lock(&locallock);
00432       cur = locals;
00433       while(cur) {
00434          if (cur == p) {
00435             if (prev)
00436                prev->next = cur->next;
00437             else
00438                locals = cur->next;
00439             break;
00440          }
00441          prev = cur;
00442          cur = cur->next;
00443       }
00444       ast_mutex_unlock(&locallock);
00445       /* Grab / release lock just in case */
00446       ast_mutex_lock(&p->lock);
00447       ast_mutex_unlock(&p->lock);
00448       /* And destroy */
00449       if (!glaredetect) {
00450          ast_mutex_destroy(&p->lock);
00451          free(p);
00452       }
00453       return 0;
00454    }
00455    if (p->chan && !p->launchedpbx)
00456       /* Need to actually hangup since there is no PBX */
00457       ochan = p->chan;
00458    else
00459       local_queue_frame(p, isoutbound, &f, NULL);
00460    ast_mutex_unlock(&p->lock);
00461    if (ochan)
00462       ast_hangup(ochan);
00463    return 0;
00464 }
00465 
00466 /*--- local_alloc: Create a call structure */
00467 static struct local_pvt *local_alloc(char *data, int format)
00468 {
00469    struct local_pvt *tmp;
00470    char *c;
00471    char *opts;
00472 
00473    tmp = malloc(sizeof(struct local_pvt));
00474    if (tmp) {
00475       memset(tmp, 0, sizeof(struct local_pvt));
00476       ast_mutex_init(&tmp->lock);
00477       strncpy(tmp->exten, data, sizeof(tmp->exten) - 1);
00478       opts = strchr(tmp->exten, '/');
00479       if (opts) {
00480          *opts='\0';
00481          opts++;
00482          if (strchr(opts, 'n'))
00483             tmp->nooptimization = 1;
00484       }
00485       c = strchr(tmp->exten, '@');
00486       if (c) {
00487          *c = '\0';
00488          c++;
00489          strncpy(tmp->context, c, sizeof(tmp->context) - 1);
00490       } else
00491          strncpy(tmp->context, "default", sizeof(tmp->context) - 1);
00492       tmp->reqformat = format;
00493       if (!ast_exists_extension(NULL, tmp->context, tmp->exten, 1, NULL)) {
00494          ast_log(LOG_NOTICE, "No such extension/context %s@%s creating local channel\n", tmp->exten, tmp->context);
00495          ast_mutex_destroy(&tmp->lock);
00496          free(tmp);
00497          tmp = NULL;
00498       } else {
00499          /* Add to list */
00500          ast_mutex_lock(&locallock);
00501          tmp->next = locals;
00502          locals = tmp;
00503          ast_mutex_unlock(&locallock);
00504       }
00505       
00506    }
00507    return tmp;
00508 }
00509 
00510 /*--- local_new: Start new local channel */
00511 static struct ast_channel *local_new(struct local_pvt *p, int state)
00512 {
00513    struct ast_channel *tmp, *tmp2;
00514    int randnum = rand() & 0xffff;
00515 
00516    tmp = ast_channel_alloc(1);
00517    tmp2 = ast_channel_alloc(1);
00518    if (!tmp || !tmp2) {
00519       if (tmp)
00520          ast_channel_free(tmp);
00521       if (tmp2)
00522          ast_channel_free(tmp2);
00523       ast_log(LOG_WARNING, "Unable to allocate channel structure(s)\n");
00524       return NULL;
00525    } 
00526 
00527    tmp2->tech = tmp->tech = &local_tech;
00528    tmp->nativeformats = p->reqformat;
00529    tmp2->nativeformats = p->reqformat;
00530    snprintf(tmp->name, sizeof(tmp->name), "Local/%s@%s-%04x,1", p->exten, p->context, randnum);
00531    snprintf(tmp2->name, sizeof(tmp2->name), "Local/%s@%s-%04x,2", p->exten, p->context, randnum);
00532    tmp->type = type;
00533    tmp2->type = type;
00534    ast_setstate(tmp, state);
00535    ast_setstate(tmp2, AST_STATE_RING);
00536    tmp->writeformat = p->reqformat;
00537    tmp2->writeformat = p->reqformat;
00538    tmp->rawwriteformat = p->reqformat;
00539    tmp2->rawwriteformat = p->reqformat;
00540    tmp->readformat = p->reqformat;
00541    tmp2->readformat = p->reqformat;
00542    tmp->rawreadformat = p->reqformat;
00543    tmp2->rawreadformat = p->reqformat;
00544    tmp->tech_pvt = p;
00545    tmp2->tech_pvt = p;
00546    p->owner = tmp;
00547    p->chan = tmp2;
00548    ast_mutex_lock(&usecnt_lock);
00549    usecnt++;
00550    usecnt++;
00551    ast_mutex_unlock(&usecnt_lock);
00552    ast_update_use_count();
00553    ast_copy_string(tmp->context, p->context, sizeof(tmp->context));
00554    ast_copy_string(tmp2->context, p->context, sizeof(tmp2->context));
00555    ast_copy_string(tmp2->exten, p->exten, sizeof(tmp->exten));
00556    tmp->priority = 1;
00557    tmp2->priority = 1;
00558 
00559    return tmp;
00560 }
00561 
00562 
00563 /*--- local_request: Part of PBX interface */
00564 static struct ast_channel *local_request(const char *type, int format, void *data, int *cause)
00565 {
00566    struct local_pvt *p;
00567    struct ast_channel *chan = NULL;
00568 
00569    p = local_alloc(data, format);
00570    if (p)
00571       chan = local_new(p, AST_STATE_DOWN);
00572    return chan;
00573 }
00574 
00575 /*--- locals_show: CLI command "local show channels" */
00576 static int locals_show(int fd, int argc, char **argv)
00577 {
00578    struct local_pvt *p;
00579 
00580    if (argc != 3)
00581       return RESULT_SHOWUSAGE;
00582    ast_mutex_lock(&locallock);
00583    p = locals;
00584    while(p) {
00585       ast_mutex_lock(&p->lock);
00586       ast_cli(fd, "%s -- %s@%s\n", p->owner ? p->owner->name : "<unowned>", p->exten, p->context);
00587       ast_mutex_unlock(&p->lock);
00588       p = p->next;
00589    }
00590    if (!locals)
00591       ast_cli(fd, "No local channels in use\n");
00592    ast_mutex_unlock(&locallock);
00593    return RESULT_SUCCESS;
00594 }
00595 
00596 static char show_locals_usage[] = 
00597 "Usage: local show channels\n"
00598 "       Provides summary information on active local proxy channels.\n";
00599 
00600 static struct ast_cli_entry cli_show_locals = {
00601    { "local", "show", "channels", NULL }, locals_show, 
00602    "Show status of local channels", show_locals_usage, NULL };
00603 
00604 /*--- load_module: Load module into PBX, register channel */
00605 int load_module()
00606 {
00607    /* Make sure we can register our channel type */
00608    if (ast_channel_register(&local_tech)) {
00609       ast_log(LOG_ERROR, "Unable to register channel class %s\n", type);
00610       return -1;
00611    }
00612    ast_cli_register(&cli_show_locals);
00613    return 0;
00614 }
00615 
00616 /*--- reload: Reload module */
00617 int reload()
00618 {
00619    return 0;
00620 }
00621 
00622 /*--- unload_module: Unload the local proxy channel from Asterisk */
00623 int unload_module()
00624 {
00625    struct local_pvt *p;
00626 
00627    /* First, take us out of the channel loop */
00628    ast_cli_unregister(&cli_show_locals);
00629    ast_channel_unregister(&local_tech);
00630    if (!ast_mutex_lock(&locallock)) {
00631       /* Hangup all interfaces if they have an owner */
00632       p = locals;
00633       while(p) {
00634          if (p->owner)
00635             ast_softhangup(p->owner, AST_SOFTHANGUP_APPUNLOAD);
00636          p = p->next;
00637       }
00638       locals = NULL;
00639       ast_mutex_unlock(&locallock);
00640    } else {
00641       ast_log(LOG_WARNING, "Unable to lock the monitor\n");
00642       return -1;
00643    }     
00644    return 0;
00645 }
00646 
00647 int usecount()
00648 {
00649    return usecnt;
00650 }
00651 
00652 char *key()
00653 {
00654    return ASTERISK_GPL_KEY;
00655 }
00656 
00657 char *description()
00658 {
00659    return (char *) desc;
00660 }
00661 

Generated on Fri May 26 01:45:30 2006 for Asterisk - the Open Source PBX by  doxygen 1.4.6