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