00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028 #include "asterisk.h"
00029
00030 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
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
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;
00107 unsigned int flags;
00108 char context[AST_MAX_CONTEXT];
00109 char exten[AST_MAX_EXTENSION];
00110 int reqformat;
00111 struct ast_channel *owner;
00112 struct ast_channel *chan;
00113 struct ast_module_user *u_owner;
00114 struct ast_module_user *u_chan;
00115 AST_LIST_ENTRY(local_pvt) list;
00116 };
00117
00118 #define LOCAL_GLARE_DETECT (1 << 0)
00119 #define LOCAL_CANCEL_QUEUE (1 << 1)
00120 #define LOCAL_ALREADY_MASQED (1 << 2)
00121 #define LOCAL_LAUNCHED_PBX (1 << 3)
00122 #define LOCAL_NO_OPTIMIZATION (1 << 4)
00123
00124 static AST_LIST_HEAD_STATIC(locals, local_pvt);
00125
00126
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
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
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, struct ast_channel *us)
00164 {
00165 struct ast_channel *other = NULL;
00166
00167
00168 other = isoutbound ? p->owner : p->chan;
00169
00170
00171 ast_set_flag(p, LOCAL_GLARE_DETECT);
00172 if (ast_test_flag(p, LOCAL_CANCEL_QUEUE)) {
00173
00174
00175 ast_mutex_unlock(&p->lock);
00176 p = local_pvt_destroy(p);
00177 return -1;
00178 }
00179 if (!other) {
00180 ast_clear_flag(p, LOCAL_GLARE_DETECT);
00181 return 0;
00182 }
00183
00184
00185 while (other && ast_channel_trylock(other)) {
00186 ast_mutex_unlock(&p->lock);
00187 if (us)
00188 ast_channel_unlock(us);
00189 usleep(1);
00190 if (us)
00191 ast_channel_lock(us);
00192 ast_mutex_lock(&p->lock);
00193 other = isoutbound ? p->owner : p->chan;
00194 }
00195
00196 if (other) {
00197 ast_queue_frame(other, f);
00198 ast_channel_unlock(other);
00199 }
00200
00201 ast_clear_flag(p, LOCAL_GLARE_DETECT);
00202
00203 return 0;
00204 }
00205
00206 static int local_answer(struct ast_channel *ast)
00207 {
00208 struct local_pvt *p = ast->tech_pvt;
00209 int isoutbound;
00210 int res = -1;
00211
00212 if (!p)
00213 return -1;
00214
00215 ast_mutex_lock(&p->lock);
00216 isoutbound = IS_OUTBOUND(ast, p);
00217 if (isoutbound) {
00218
00219 struct ast_frame answer = { AST_FRAME_CONTROL, AST_CONTROL_ANSWER };
00220 res = local_queue_frame(p, isoutbound, &answer, ast);
00221 } else
00222 ast_log(LOG_WARNING, "Huh? Local is being asked to answer?\n");
00223 if (!res)
00224 ast_mutex_unlock(&p->lock);
00225 return res;
00226 }
00227
00228 static void check_bridge(struct local_pvt *p, int isoutbound)
00229 {
00230 struct ast_channel_monitor *tmp;
00231 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)))
00232 return;
00233
00234
00235
00236
00237
00238
00239 if (isoutbound && p->chan->_bridge && AST_LIST_EMPTY(&p->owner->readq)) {
00240
00241
00242
00243
00244 if (!ast_mutex_trylock(&(p->chan->_bridge)->lock)) {
00245 if (!p->chan->_bridge->_softhangup) {
00246 if (!ast_mutex_trylock(&p->owner->lock)) {
00247 if (!p->owner->_softhangup) {
00248 if(p->owner->monitor && !p->chan->_bridge->monitor) {
00249
00250
00251
00252
00253
00254 tmp = p->owner->monitor;
00255 p->owner->monitor = p->chan->_bridge->monitor;
00256 p->chan->_bridge->monitor = tmp;
00257 }
00258 ast_channel_masquerade(p->owner, p->chan->_bridge);
00259 ast_set_flag(p, LOCAL_ALREADY_MASQED);
00260 }
00261 ast_mutex_unlock(&p->owner->lock);
00262 }
00263 ast_mutex_unlock(&(p->chan->_bridge)->lock);
00264 }
00265 }
00266
00267
00268
00269
00270 #if 0
00271 } else if (!isoutbound && p->owner && p->owner->_bridge && p->chan && AST_LIST_EMPTY(&p->chan->readq)) {
00272
00273 if (!ast_mutex_trylock(&(p->owner->_bridge)->lock)) {
00274 if (!p->owner->_bridge->_softhangup) {
00275 if (!ast_mutex_trylock(&p->chan->lock)) {
00276 if (!p->chan->_softhangup) {
00277 ast_channel_masquerade(p->chan, p->owner->_bridge);
00278 ast_set_flag(p, LOCAL_ALREADY_MASQED);
00279 }
00280 ast_mutex_unlock(&p->chan->lock);
00281 }
00282 }
00283 ast_mutex_unlock(&(p->owner->_bridge)->lock);
00284 }
00285 #endif
00286 }
00287 }
00288
00289 static struct ast_frame *local_read(struct ast_channel *ast)
00290 {
00291 return &ast_null_frame;
00292 }
00293
00294 static int local_write(struct ast_channel *ast, struct ast_frame *f)
00295 {
00296 struct local_pvt *p = ast->tech_pvt;
00297 int res = -1;
00298 int isoutbound;
00299
00300 if (!p)
00301 return -1;
00302
00303
00304 ast_mutex_lock(&p->lock);
00305 isoutbound = IS_OUTBOUND(ast, p);
00306 if (f && (f->frametype == AST_FRAME_VOICE || f->frametype == AST_FRAME_VIDEO))
00307 check_bridge(p, isoutbound);
00308 if (!ast_test_flag(p, LOCAL_ALREADY_MASQED))
00309 res = local_queue_frame(p, isoutbound, f, ast);
00310 else {
00311 if (option_debug)
00312 ast_log(LOG_DEBUG, "Not posting to queue since already masked on '%s'\n", ast->name);
00313 res = 0;
00314 }
00315 if (!res)
00316 ast_mutex_unlock(&p->lock);
00317 return res;
00318 }
00319
00320 static int local_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
00321 {
00322 struct local_pvt *p = newchan->tech_pvt;
00323
00324 if (!p)
00325 return -1;
00326
00327 ast_mutex_lock(&p->lock);
00328
00329 if ((p->owner != oldchan) && (p->chan != oldchan)) {
00330 ast_log(LOG_WARNING, "Old channel wasn't %p but was %p/%p\n", oldchan, p->owner, p->chan);
00331 ast_mutex_unlock(&p->lock);
00332 return -1;
00333 }
00334 if (p->owner == oldchan)
00335 p->owner = newchan;
00336 else
00337 p->chan = newchan;
00338 ast_mutex_unlock(&p->lock);
00339 return 0;
00340 }
00341
00342 static int local_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen)
00343 {
00344 struct local_pvt *p = ast->tech_pvt;
00345 int res = 0;
00346 struct ast_frame f = { AST_FRAME_CONTROL, };
00347 int isoutbound;
00348
00349 if (!p)
00350 return -1;
00351
00352
00353 if (condition == AST_CONTROL_HOLD) {
00354 ast_moh_start(ast, data, NULL);
00355 } else if (condition == AST_CONTROL_UNHOLD) {
00356 ast_moh_stop(ast);
00357 } else {
00358
00359 ast_mutex_lock(&p->lock);
00360 isoutbound = IS_OUTBOUND(ast, p);
00361 f.subclass = condition;
00362 f.data = (void*)data;
00363 f.datalen = datalen;
00364 if (!(res = local_queue_frame(p, isoutbound, &f, ast)))
00365 ast_mutex_unlock(&p->lock);
00366 }
00367
00368 return res;
00369 }
00370
00371 static int local_digit_begin(struct ast_channel *ast, char digit)
00372 {
00373 struct local_pvt *p = ast->tech_pvt;
00374 int res = -1;
00375 struct ast_frame f = { AST_FRAME_DTMF_BEGIN, };
00376 int isoutbound;
00377
00378 if (!p)
00379 return -1;
00380
00381 ast_mutex_lock(&p->lock);
00382 isoutbound = IS_OUTBOUND(ast, p);
00383 f.subclass = digit;
00384 if (!(res = local_queue_frame(p, isoutbound, &f, ast)))
00385 ast_mutex_unlock(&p->lock);
00386
00387 return res;
00388 }
00389
00390 static int local_digit_end(struct ast_channel *ast, char digit, unsigned int duration)
00391 {
00392 struct local_pvt *p = ast->tech_pvt;
00393 int res = -1;
00394 struct ast_frame f = { AST_FRAME_DTMF_END, };
00395 int isoutbound;
00396
00397 if (!p)
00398 return -1;
00399
00400 ast_mutex_lock(&p->lock);
00401 isoutbound = IS_OUTBOUND(ast, p);
00402 f.subclass = digit;
00403 f.len = duration;
00404 if (!(res = local_queue_frame(p, isoutbound, &f, ast)))
00405 ast_mutex_unlock(&p->lock);
00406
00407 return res;
00408 }
00409
00410 static int local_sendtext(struct ast_channel *ast, const char *text)
00411 {
00412 struct local_pvt *p = ast->tech_pvt;
00413 int res = -1;
00414 struct ast_frame f = { AST_FRAME_TEXT, };
00415 int isoutbound;
00416
00417 if (!p)
00418 return -1;
00419
00420 ast_mutex_lock(&p->lock);
00421 isoutbound = IS_OUTBOUND(ast, p);
00422 f.data = (char *) text;
00423 f.datalen = strlen(text) + 1;
00424 if (!(res = local_queue_frame(p, isoutbound, &f, ast)))
00425 ast_mutex_unlock(&p->lock);
00426 return res;
00427 }
00428
00429 static int local_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen)
00430 {
00431 struct local_pvt *p = ast->tech_pvt;
00432 int res = -1;
00433 struct ast_frame f = { AST_FRAME_HTML, };
00434 int isoutbound;
00435
00436 if (!p)
00437 return -1;
00438
00439 ast_mutex_lock(&p->lock);
00440 isoutbound = IS_OUTBOUND(ast, p);
00441 f.subclass = subclass;
00442 f.data = (char *)data;
00443 f.datalen = datalen;
00444 if (!(res = local_queue_frame(p, isoutbound, &f, ast)))
00445 ast_mutex_unlock(&p->lock);
00446 return res;
00447 }
00448
00449
00450
00451 static int local_call(struct ast_channel *ast, char *dest, int timeout)
00452 {
00453 struct local_pvt *p = ast->tech_pvt;
00454 int res;
00455 struct ast_var_t *varptr = NULL, *new;
00456 size_t len, namelen;
00457
00458 if (!p)
00459 return -1;
00460
00461 ast_mutex_lock(&p->lock);
00462
00463
00464
00465
00466
00467 p->chan->cid.cid_num = ast_strdup(p->owner->cid.cid_num);
00468 p->chan->cid.cid_name = ast_strdup(p->owner->cid.cid_name);
00469 p->chan->cid.cid_rdnis = ast_strdup(p->owner->cid.cid_rdnis);
00470 p->chan->cid.cid_ani = ast_strdup(p->owner->cid.cid_ani);
00471 p->chan->cid.cid_pres = p->owner->cid.cid_pres;
00472 ast_string_field_set(p->chan, language, p->owner->language);
00473 ast_string_field_set(p->chan, accountcode, p->owner->accountcode);
00474 p->chan->cdrflags = p->owner->cdrflags;
00475
00476
00477
00478 AST_LIST_TRAVERSE(&p->owner->varshead, varptr, entries) {
00479 namelen = strlen(varptr->name);
00480 len = sizeof(struct ast_var_t) + namelen + strlen(varptr->value) + 2;
00481 if ((new = ast_calloc(1, len))) {
00482 memcpy(new, varptr, len);
00483 new->value = &(new->name[0]) + namelen + 1;
00484 AST_LIST_INSERT_TAIL(&p->chan->varshead, new, entries);
00485 }
00486 }
00487 ast_channel_datastore_inherit(p->owner, p->chan);
00488
00489
00490 if (!(res = ast_pbx_start(p->chan)))
00491 ast_set_flag(p, LOCAL_LAUNCHED_PBX);
00492
00493 ast_mutex_unlock(&p->lock);
00494 return res;
00495 }
00496
00497
00498 static int local_hangup(struct ast_channel *ast)
00499 {
00500 struct local_pvt *p = ast->tech_pvt;
00501 int isoutbound;
00502 struct ast_frame f = { AST_FRAME_CONTROL, AST_CONTROL_HANGUP };
00503 struct ast_channel *ochan = NULL;
00504 int glaredetect = 0, res = 0;
00505
00506 if (!p)
00507 return -1;
00508
00509 ast_mutex_lock(&p->lock);
00510 isoutbound = IS_OUTBOUND(ast, p);
00511 if (isoutbound) {
00512 const char *status = pbx_builtin_getvar_helper(p->chan, "DIALSTATUS");
00513 if ((status) && (p->owner)) {
00514
00515 while (p->owner && ast_channel_trylock(p->owner)) {
00516 ast_mutex_unlock(&p->lock);
00517 usleep(1);
00518 ast_mutex_lock(&p->lock);
00519 }
00520 if (p->owner) {
00521 pbx_builtin_setvar_helper(p->owner, "CHANLOCALSTATUS", status);
00522 ast_channel_unlock(p->owner);
00523 }
00524 }
00525 p->chan = NULL;
00526 ast_clear_flag(p, LOCAL_LAUNCHED_PBX);
00527 ast_module_user_remove(p->u_chan);
00528 } else {
00529 p->owner = NULL;
00530 ast_module_user_remove(p->u_owner);
00531 }
00532
00533 ast->tech_pvt = NULL;
00534
00535 if (!p->owner && !p->chan) {
00536
00537 glaredetect = ast_test_flag(p, LOCAL_GLARE_DETECT);
00538
00539
00540 if (glaredetect)
00541 ast_set_flag(p, LOCAL_CANCEL_QUEUE);
00542 ast_mutex_unlock(&p->lock);
00543
00544 AST_LIST_LOCK(&locals);
00545 AST_LIST_REMOVE(&locals, p, list);
00546 AST_LIST_UNLOCK(&locals);
00547
00548 ast_mutex_lock(&p->lock);
00549 ast_mutex_unlock(&p->lock);
00550
00551 if (!glaredetect) {
00552 p = local_pvt_destroy(p);
00553 }
00554 return 0;
00555 }
00556 if (p->chan && !ast_test_flag(p, LOCAL_LAUNCHED_PBX))
00557
00558 ochan = p->chan;
00559 else
00560 res = local_queue_frame(p, isoutbound, &f, NULL);
00561 if (!res)
00562 ast_mutex_unlock(&p->lock);
00563 if (ochan)
00564 ast_hangup(ochan);
00565 return 0;
00566 }
00567
00568
00569 static struct local_pvt *local_alloc(const char *data, int format)
00570 {
00571 struct local_pvt *tmp = NULL;
00572 char *c = NULL, *opts = NULL;
00573
00574 if (!(tmp = ast_calloc(1, sizeof(*tmp))))
00575 return NULL;
00576
00577
00578 ast_mutex_init(&tmp->lock);
00579 ast_copy_string(tmp->exten, data, sizeof(tmp->exten));
00580
00581
00582 if ((opts = strchr(tmp->exten, '/'))) {
00583 *opts++ = '\0';
00584 if (strchr(opts, 'n'))
00585 ast_set_flag(tmp, LOCAL_NO_OPTIMIZATION);
00586 }
00587
00588
00589 if ((c = strchr(tmp->exten, '@')))
00590 *c++ = '\0';
00591
00592 ast_copy_string(tmp->context, c ? c : "default", sizeof(tmp->context));
00593
00594 tmp->reqformat = format;
00595
00596 if (!ast_exists_extension(NULL, tmp->context, tmp->exten, 1, NULL)) {
00597 ast_log(LOG_NOTICE, "No such extension/context %s@%s creating local channel\n", tmp->exten, tmp->context);
00598 tmp = local_pvt_destroy(tmp);
00599 } else {
00600
00601 AST_LIST_LOCK(&locals);
00602 AST_LIST_INSERT_HEAD(&locals, tmp, list);
00603 AST_LIST_UNLOCK(&locals);
00604 }
00605
00606 return tmp;
00607 }
00608
00609
00610 static struct ast_channel *local_new(struct local_pvt *p, int state)
00611 {
00612 struct ast_channel *tmp = NULL, *tmp2 = NULL;
00613 int randnum = ast_random() & 0xffff, fmt = 0;
00614 const char *t;
00615 int ama;
00616
00617
00618
00619 if (p->owner && p->owner->accountcode)
00620 t = p->owner->accountcode;
00621 else
00622 t = "";
00623
00624 if (p->owner)
00625 ama = p->owner->amaflags;
00626 else
00627 ama = 0;
00628 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))
00629 || !(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))) {
00630 if (tmp)
00631 ast_channel_free(tmp);
00632 if (tmp2)
00633 ast_channel_free(tmp2);
00634 ast_log(LOG_WARNING, "Unable to allocate channel structure(s)\n");
00635 return NULL;
00636 }
00637
00638 tmp2->tech = tmp->tech = &local_tech;
00639
00640 tmp->nativeformats = p->reqformat;
00641 tmp2->nativeformats = p->reqformat;
00642
00643
00644 fmt = ast_best_codec(p->reqformat);
00645 tmp->writeformat = fmt;
00646 tmp2->writeformat = fmt;
00647 tmp->rawwriteformat = fmt;
00648 tmp2->rawwriteformat = fmt;
00649 tmp->readformat = fmt;
00650 tmp2->readformat = fmt;
00651 tmp->rawreadformat = fmt;
00652 tmp2->rawreadformat = fmt;
00653
00654 tmp->tech_pvt = p;
00655 tmp2->tech_pvt = p;
00656
00657 p->owner = tmp;
00658 p->chan = tmp2;
00659 p->u_owner = ast_module_user_add(p->owner);
00660 p->u_chan = ast_module_user_add(p->chan);
00661
00662 ast_copy_string(tmp->context, p->context, sizeof(tmp->context));
00663 ast_copy_string(tmp2->context, p->context, sizeof(tmp2->context));
00664 ast_copy_string(tmp2->exten, p->exten, sizeof(tmp->exten));
00665 tmp->priority = 1;
00666 tmp2->priority = 1;
00667
00668 return tmp;
00669 }
00670
00671
00672 static struct ast_channel *local_request(const char *type, int format, void *data, int *cause)
00673 {
00674 struct local_pvt *p = NULL;
00675 struct ast_channel *chan = NULL;
00676
00677
00678 if ((p = local_alloc(data, format))) {
00679 if (!(chan = local_new(p, AST_STATE_DOWN))) {
00680 AST_LIST_LOCK(&locals);
00681 AST_LIST_REMOVE(&locals, p, list);
00682 AST_LIST_UNLOCK(&locals);
00683 p = local_pvt_destroy(p);
00684 }
00685 }
00686
00687 return chan;
00688 }
00689
00690
00691 static int locals_show(int fd, int argc, char **argv)
00692 {
00693 struct local_pvt *p = NULL;
00694
00695 if (argc != 3)
00696 return RESULT_SHOWUSAGE;
00697
00698 AST_LIST_LOCK(&locals);
00699 if (!AST_LIST_EMPTY(&locals)) {
00700 AST_LIST_TRAVERSE(&locals, p, list) {
00701 ast_mutex_lock(&p->lock);
00702 ast_cli(fd, "%s -- %s@%s\n", p->owner ? p->owner->name : "<unowned>", p->exten, p->context);
00703 ast_mutex_unlock(&p->lock);
00704 }
00705 } else
00706 ast_cli(fd, "No local channels in use\n");
00707 AST_LIST_UNLOCK(&locals);
00708
00709 return RESULT_SUCCESS;
00710 }
00711
00712 static char show_locals_usage[] =
00713 "Usage: local show channels\n"
00714 " Provides summary information on active local proxy channels.\n";
00715
00716 static struct ast_cli_entry cli_local[] = {
00717 { { "local", "show", "channels", NULL },
00718 locals_show, "List status of local channels",
00719 show_locals_usage },
00720 };
00721
00722
00723 static int load_module(void)
00724 {
00725
00726 if (ast_channel_register(&local_tech)) {
00727 ast_log(LOG_ERROR, "Unable to register channel class 'Local'\n");
00728 return -1;
00729 }
00730 ast_cli_register_multiple(cli_local, sizeof(cli_local) / sizeof(struct ast_cli_entry));
00731 return 0;
00732 }
00733
00734
00735 static int unload_module(void)
00736 {
00737 struct local_pvt *p = NULL;
00738
00739
00740 ast_cli_unregister_multiple(cli_local, sizeof(cli_local) / sizeof(struct ast_cli_entry));
00741 ast_channel_unregister(&local_tech);
00742 if (!AST_LIST_LOCK(&locals)) {
00743
00744 AST_LIST_TRAVERSE(&locals, p, list) {
00745 if (p->owner)
00746 ast_softhangup(p->owner, AST_SOFTHANGUP_APPUNLOAD);
00747 }
00748 AST_LIST_UNLOCK(&locals);
00749 AST_LIST_HEAD_DESTROY(&locals);
00750 } else {
00751 ast_log(LOG_WARNING, "Unable to lock the monitor\n");
00752 return -1;
00753 }
00754 return 0;
00755 }
00756
00757 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Local Proxy Channel (Note: used internally by other modules)");