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: 316328 $")
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 #include "asterisk/astobj2.h"
00065
00066 static const char tdesc[] = "Local Proxy Channel Driver";
00067
00068 #define IS_OUTBOUND(a,b) (a == b->chan ? 1 : 0)
00069
00070
00071
00072
00073 static const int BUCKET_SIZE = 1;
00074
00075 static struct ao2_container *locals;
00076
00077 static struct ast_channel *local_request(const char *type, int format, void *data, int *cause);
00078 static int local_digit_begin(struct ast_channel *ast, char digit);
00079 static int local_digit_end(struct ast_channel *ast, char digit, unsigned int duration);
00080 static int local_call(struct ast_channel *ast, char *dest, int timeout);
00081 static int local_hangup(struct ast_channel *ast);
00082 static int local_answer(struct ast_channel *ast);
00083 static struct ast_frame *local_read(struct ast_channel *ast);
00084 static int local_write(struct ast_channel *ast, struct ast_frame *f);
00085 static int local_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen);
00086 static int local_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
00087 static int local_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen);
00088 static int local_sendtext(struct ast_channel *ast, const char *text);
00089 static int local_devicestate(void *data);
00090 static int local_setoption(struct ast_channel *chan, int option, void *data, int datalen);
00091
00092
00093 static const struct ast_channel_tech local_tech = {
00094 .type = "Local",
00095 .description = tdesc,
00096 .capabilities = -1,
00097 .requester = local_request,
00098 .send_digit_begin = local_digit_begin,
00099 .send_digit_end = local_digit_end,
00100 .call = local_call,
00101 .hangup = local_hangup,
00102 .answer = local_answer,
00103 .read = local_read,
00104 .write = local_write,
00105 .write_video = local_write,
00106 .exception = local_read,
00107 .indicate = local_indicate,
00108 .fixup = local_fixup,
00109 .send_html = local_sendhtml,
00110 .send_text = local_sendtext,
00111 .devicestate = local_devicestate,
00112 .setoption = local_setoption,
00113 };
00114
00115 struct local_pvt {
00116 unsigned int flags;
00117 char context[AST_MAX_CONTEXT];
00118 char exten[AST_MAX_EXTENSION];
00119 int reqformat;
00120 struct ast_channel *owner;
00121 struct ast_channel *chan;
00122 struct ast_module_user *u_owner;
00123 struct ast_module_user *u_chan;
00124 AST_LIST_ENTRY(local_pvt) list;
00125 };
00126
00127 #define LOCAL_ALREADY_MASQED (1 << 1)
00128 #define LOCAL_LAUNCHED_PBX (1 << 2)
00129 #define LOCAL_NO_OPTIMIZATION (1 << 3)
00130 #define LOCAL_MOH_PASSTHRU (1 << 4)
00131
00132 static int local_setoption(struct ast_channel *chan, int option, void * data, int datalen)
00133 {
00134 int res;
00135 struct local_pvt *p;
00136 struct ast_channel *otherchan;
00137 ast_chan_write_info_t *write_info;
00138
00139 if (option != AST_OPTION_CHANNEL_WRITE) {
00140 return -1;
00141 }
00142
00143 write_info = data;
00144
00145 if (write_info->version != AST_CHAN_WRITE_INFO_T_VERSION) {
00146 ast_log(LOG_ERROR, "The chan_write_info_t type has changed, and this channel hasn't been updated!\n");
00147 return -1;
00148 }
00149
00150
00151 startover:
00152 ast_channel_lock(chan);
00153
00154 p = chan->tech_pvt;
00155 if (!p) {
00156 ast_channel_unlock(chan);
00157 ast_log(LOG_WARNING, "Could not update other side of %s, local_pvt went away.\n", chan->name);
00158 return -1;
00159 }
00160
00161 while (ao2_trylock(p)) {
00162 ast_channel_unlock(chan);
00163 sched_yield();
00164 ast_channel_lock(chan);
00165 p = chan->tech_pvt;
00166 if (!p) {
00167 ast_channel_unlock(chan);
00168 ast_log(LOG_WARNING, "Could not update other side of %s, local_pvt went away.\n", chan->name);
00169 return -1;
00170 }
00171 }
00172
00173 otherchan = (write_info->chan == p->owner) ? p->chan : p->owner;
00174
00175 if (!otherchan || otherchan == write_info->chan) {
00176 ao2_unlock(p);
00177 ast_channel_unlock(chan);
00178 ast_log(LOG_WARNING, "Could not update other side of %s, other side went away.\n", chan->name);
00179 return 0;
00180 }
00181
00182 if (ast_channel_trylock(otherchan)) {
00183 ao2_unlock(p);
00184 ast_channel_unlock(chan);
00185 goto startover;
00186 }
00187
00188 res = write_info->write_fn(otherchan, write_info->function, write_info->data, write_info->value);
00189
00190 ast_channel_unlock(otherchan);
00191 ao2_unlock(p);
00192 ast_channel_unlock(chan);
00193
00194 return res;
00195 }
00196
00197
00198 static int local_devicestate(void *data)
00199 {
00200 char *exten = ast_strdupa(data);
00201 char *context = NULL, *opts = NULL;
00202 int res;
00203
00204 if (!(context = strchr(exten, '@'))) {
00205 ast_log(LOG_WARNING, "Someone used Local/%s somewhere without a @context. This is bad.\n", exten);
00206 return AST_DEVICE_INVALID;
00207 }
00208
00209 *context++ = '\0';
00210
00211
00212 if ((opts = strchr(context, '/')))
00213 *opts = '\0';
00214
00215 if (option_debug > 2)
00216 ast_log(LOG_DEBUG, "Checking if extension %s@%s exists (devicestate)\n", exten, context);
00217 res = ast_exists_extension(NULL, context, exten, 1, NULL);
00218 if (!res)
00219 return AST_DEVICE_INVALID;
00220 else
00221 return AST_DEVICE_UNKNOWN;
00222 }
00223
00224
00225
00226
00227
00228
00229
00230
00231
00232 static int local_queue_frame(struct local_pvt *p, int isoutbound, struct ast_frame *f,
00233 struct ast_channel *us, int us_locked)
00234 {
00235 struct ast_channel *other = NULL;
00236
00237
00238 other = isoutbound ? p->owner : p->chan;
00239
00240 if (!other) {
00241 return 0;
00242 }
00243
00244
00245 if (us && us->generator && other->generator) {
00246 return 0;
00247 }
00248
00249
00250 while (other && ast_channel_trylock(other)) {
00251 int res;
00252 if ((res = ao2_unlock(p))) {
00253 ast_log(LOG_ERROR, "chan_local bug! '&p->lock' was not locked when entering local_queue_frame! (%s)\n", strerror(res));
00254 return -1;
00255 }
00256 if (us && us_locked) {
00257 do {
00258 if (ast_channel_unlock(us)) {
00259 ast_log(LOG_ERROR, "chan_local bug! Our channel was not locked, yet arguments indicated that it was!!\n");
00260 ao2_lock(p);
00261 return -1;
00262 }
00263 usleep(1);
00264 ast_channel_lock(us);
00265 } while (ao2_trylock(p));
00266 } else {
00267 usleep(1);
00268 ao2_lock(p);
00269 }
00270 other = isoutbound ? p->owner : p->chan;
00271 }
00272
00273 if (other) {
00274 if (f->frametype == AST_FRAME_CONTROL && f->subclass == AST_CONTROL_RINGING) {
00275 ast_setstate(other, AST_STATE_RINGING);
00276 }
00277 ast_queue_frame(other, f);
00278 ast_channel_unlock(other);
00279 }
00280
00281 return 0;
00282 }
00283
00284 static int local_answer(struct ast_channel *ast)
00285 {
00286 struct local_pvt *p = ast->tech_pvt;
00287 int isoutbound;
00288 int res = -1;
00289
00290 if (!p)
00291 return -1;
00292
00293 ao2_lock(p);
00294 ao2_ref(p, 1);
00295 isoutbound = IS_OUTBOUND(ast, p);
00296 if (isoutbound) {
00297
00298 struct ast_frame answer = { AST_FRAME_CONTROL, AST_CONTROL_ANSWER };
00299 res = local_queue_frame(p, isoutbound, &answer, ast, 1);
00300 } else {
00301 ast_log(LOG_WARNING, "Huh? Local is being asked to answer?\n");
00302 }
00303 ao2_unlock(p);
00304 ao2_ref(p, -1);
00305 return res;
00306 }
00307
00308
00309
00310
00311
00312 static void check_bridge(struct local_pvt *p)
00313 {
00314 struct ast_channel_monitor *tmp;
00315 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)))
00316 return;
00317
00318
00319
00320
00321
00322
00323 if (p->chan->_bridge && AST_LIST_EMPTY(&p->owner->readq)) {
00324
00325
00326
00327
00328 if (!ast_mutex_trylock(&(p->chan->_bridge)->lock)) {
00329 if (!p->chan->_bridge->_softhangup) {
00330 if (!ast_mutex_trylock(&p->owner->lock)) {
00331 if (!p->owner->_softhangup) {
00332 if (p->owner->monitor && !p->chan->_bridge->monitor) {
00333
00334
00335
00336
00337
00338 tmp = p->owner->monitor;
00339 p->owner->monitor = p->chan->_bridge->monitor;
00340 p->chan->_bridge->monitor = tmp;
00341 }
00342 if (p->chan->audiohooks) {
00343 struct ast_audiohook_list *audiohooks_swapper;
00344 audiohooks_swapper = p->chan->audiohooks;
00345 p->chan->audiohooks = p->owner->audiohooks;
00346 p->owner->audiohooks = audiohooks_swapper;
00347 }
00348
00349
00350
00351
00352
00353
00354
00355
00356 if (p->owner->cid.cid_dnid || p->owner->cid.cid_num ||
00357 p->owner->cid.cid_name || p->owner->cid.cid_ani ||
00358 p->owner->cid.cid_rdnis || p->owner->cid.cid_pres ||
00359 p->owner->cid.cid_ani2 || p->owner->cid.cid_ton ||
00360 p->owner->cid.cid_tns) {
00361
00362 struct ast_callerid tmpcid;
00363 tmpcid = p->owner->cid;
00364 p->owner->cid = p->chan->_bridge->cid;
00365 p->chan->_bridge->cid = tmpcid;
00366 }
00367
00368 ast_app_group_update(p->chan, p->owner);
00369 ast_channel_masquerade(p->owner, p->chan->_bridge);
00370 ast_set_flag(p, LOCAL_ALREADY_MASQED);
00371 }
00372 ast_mutex_unlock(&p->owner->lock);
00373 }
00374 }
00375 ast_mutex_unlock(&(p->chan->_bridge)->lock);
00376 }
00377 }
00378 }
00379
00380 static struct ast_frame *local_read(struct ast_channel *ast)
00381 {
00382 return &ast_null_frame;
00383 }
00384
00385 static int local_write(struct ast_channel *ast, struct ast_frame *f)
00386 {
00387 struct local_pvt *p = ast->tech_pvt;
00388 int res = -1;
00389 int isoutbound;
00390
00391 if (!p)
00392 return -1;
00393
00394
00395 ao2_lock(p);
00396 ao2_ref(p, 1);
00397 isoutbound = IS_OUTBOUND(ast, p);
00398 if (isoutbound && f && (f->frametype == AST_FRAME_VOICE || f->frametype == AST_FRAME_VIDEO))
00399 check_bridge(p);
00400 if (!ast_test_flag(p, LOCAL_ALREADY_MASQED))
00401 res = local_queue_frame(p, isoutbound, f, ast, 1);
00402 else {
00403 if (option_debug)
00404 ast_log(LOG_DEBUG, "Not posting to queue since already masked on '%s'\n", ast->name);
00405 res = 0;
00406 }
00407 ao2_unlock(p);
00408 ao2_ref(p, -1);
00409
00410 return res;
00411 }
00412
00413 static int local_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
00414 {
00415 struct local_pvt *p = newchan->tech_pvt;
00416
00417 if (!p)
00418 return -1;
00419
00420 ao2_lock(p);
00421
00422 if ((p->owner != oldchan) && (p->chan != oldchan)) {
00423 ast_log(LOG_WARNING, "Old channel wasn't %p but was %p/%p\n", oldchan, p->owner, p->chan);
00424 ao2_unlock(p);
00425 return -1;
00426 }
00427 if (p->owner == oldchan)
00428 p->owner = newchan;
00429 else
00430 p->chan = newchan;
00431
00432
00433 if (!ast_check_hangup(newchan) && ((p->owner && p->owner->_bridge == p->chan) || (p->chan && p->chan->_bridge == p->owner))) {
00434 ast_log(LOG_WARNING, "You can not bridge a Local channel to itself!\n");
00435 ao2_unlock(p);
00436 ast_queue_hangup(newchan);
00437 return -1;
00438 }
00439
00440 ao2_unlock(p);
00441 return 0;
00442 }
00443
00444 static int local_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen)
00445 {
00446 struct local_pvt *p = ast->tech_pvt;
00447 int res = 0;
00448 struct ast_frame f = { AST_FRAME_CONTROL, };
00449 int isoutbound;
00450
00451 if (!p)
00452 return -1;
00453
00454 ao2_ref(p, 1);
00455
00456
00457 if (!ast_test_flag(p, LOCAL_MOH_PASSTHRU) && condition == AST_CONTROL_HOLD) {
00458 ast_moh_start(ast, data, NULL);
00459 } else if (!ast_test_flag(p, LOCAL_MOH_PASSTHRU) && condition == AST_CONTROL_UNHOLD) {
00460 ast_moh_stop(ast);
00461 } else {
00462
00463 ao2_lock(p);
00464 isoutbound = IS_OUTBOUND(ast, p);
00465 f.subclass = condition;
00466 f.data = (void*)data;
00467 f.datalen = datalen;
00468 res = local_queue_frame(p, isoutbound, &f, ast, 1);
00469 ao2_unlock(p);
00470 }
00471 ao2_ref(p, -1);
00472
00473 return res;
00474 }
00475
00476 static int local_digit_begin(struct ast_channel *ast, char digit)
00477 {
00478 struct local_pvt *p = ast->tech_pvt;
00479 int res = -1;
00480 struct ast_frame f = { AST_FRAME_DTMF_BEGIN, };
00481 int isoutbound;
00482
00483 if (!p)
00484 return -1;
00485
00486 ao2_ref(p, 1);
00487 ao2_lock(p);
00488 isoutbound = IS_OUTBOUND(ast, p);
00489 f.subclass = digit;
00490 res = local_queue_frame(p, isoutbound, &f, ast, 0);
00491 ao2_unlock(p);
00492 ao2_ref(p, -1);
00493
00494 return res;
00495 }
00496
00497 static int local_digit_end(struct ast_channel *ast, char digit, unsigned int duration)
00498 {
00499 struct local_pvt *p = ast->tech_pvt;
00500 int res = -1;
00501 struct ast_frame f = { AST_FRAME_DTMF_END, };
00502 int isoutbound;
00503
00504 if (!p)
00505 return -1;
00506
00507 ao2_lock(p);
00508 ao2_ref(p, 1);
00509 isoutbound = IS_OUTBOUND(ast, p);
00510 f.subclass = digit;
00511 f.len = duration;
00512 res = local_queue_frame(p, isoutbound, &f, ast, 0);
00513 ao2_unlock(p);
00514 ao2_ref(p, -1);
00515
00516 return res;
00517 }
00518
00519 static int local_sendtext(struct ast_channel *ast, const char *text)
00520 {
00521 struct local_pvt *p = ast->tech_pvt;
00522 int res = -1;
00523 struct ast_frame f = { AST_FRAME_TEXT, };
00524 int isoutbound;
00525
00526 if (!p)
00527 return -1;
00528
00529 ao2_lock(p);
00530 ao2_ref(p, 1);
00531 isoutbound = IS_OUTBOUND(ast, p);
00532 f.data = (char *) text;
00533 f.datalen = strlen(text) + 1;
00534 res = local_queue_frame(p, isoutbound, &f, ast, 0);
00535 ao2_unlock(p);
00536 ao2_ref(p, -1);
00537 return res;
00538 }
00539
00540 static int local_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen)
00541 {
00542 struct local_pvt *p = ast->tech_pvt;
00543 int res = -1;
00544 struct ast_frame f = { AST_FRAME_HTML, };
00545 int isoutbound;
00546
00547 if (!p)
00548 return -1;
00549
00550 ao2_lock(p);
00551 ao2_ref(p, 1);
00552
00553 isoutbound = IS_OUTBOUND(ast, p);
00554 f.subclass = subclass;
00555 f.data = (char *)data;
00556 f.datalen = datalen;
00557 res = local_queue_frame(p, isoutbound, &f, ast, 0);
00558
00559 ao2_unlock(p);
00560 ao2_ref(p, -1);
00561
00562 return res;
00563 }
00564
00565
00566
00567 static int local_call(struct ast_channel *ast, char *dest, int timeout)
00568 {
00569 struct local_pvt *p = ast->tech_pvt;
00570 int res = 0;
00571 struct ast_var_t *varptr = NULL, *new;
00572 size_t len, namelen;
00573
00574 if (!p)
00575 return -1;
00576
00577 ao2_lock(p);
00578
00579
00580
00581
00582
00583 p->chan->cid.cid_dnid = ast_strdup(p->owner->cid.cid_dnid);
00584 p->chan->cid.cid_num = ast_strdup(p->owner->cid.cid_num);
00585 p->chan->cid.cid_name = ast_strdup(p->owner->cid.cid_name);
00586 p->chan->cid.cid_rdnis = ast_strdup(p->owner->cid.cid_rdnis);
00587 p->chan->cid.cid_ani = ast_strdup(p->owner->cid.cid_ani);
00588 p->chan->cid.cid_pres = p->owner->cid.cid_pres;
00589 p->chan->cid.cid_ani2 = p->owner->cid.cid_ani2;
00590 p->chan->cid.cid_ton = p->owner->cid.cid_ton;
00591 p->chan->cid.cid_tns = p->owner->cid.cid_tns;
00592 ast_string_field_set(p->chan, language, p->owner->language);
00593 ast_string_field_set(p->chan, accountcode, p->owner->accountcode);
00594 ast_string_field_set(p->chan, musicclass, p->owner->musicclass);
00595 ast_cdr_update(p->chan);
00596 p->chan->cdrflags = p->owner->cdrflags;
00597
00598
00599
00600 AST_LIST_TRAVERSE(&p->owner->varshead, varptr, entries) {
00601 namelen = strlen(varptr->name);
00602 len = sizeof(struct ast_var_t) + namelen + strlen(varptr->value) + 2;
00603 if ((new = ast_calloc(1, len))) {
00604 memcpy(new, varptr, len);
00605 new->value = &(new->name[0]) + namelen + 1;
00606 AST_LIST_INSERT_TAIL(&p->chan->varshead, new, entries);
00607 }
00608 }
00609 ast_channel_datastore_inherit(p->owner, p->chan);
00610
00611 if (!ast_exists_extension(p->chan, p->chan->context, p->chan->exten, 1, p->owner->cid.cid_num)) {
00612 ast_log(LOG_NOTICE, "No such extension/context %s@%s while calling Local channel\n", p->chan->exten, p->chan->context);
00613 ao2_unlock(p);
00614 return -1;
00615 }
00616
00617
00618 if (!(res = ast_pbx_start(p->chan)))
00619 ast_set_flag(p, LOCAL_LAUNCHED_PBX);
00620
00621 ao2_unlock(p);
00622 return res;
00623 }
00624
00625
00626 static int local_hangup(struct ast_channel *ast)
00627 {
00628 struct local_pvt *p = ast->tech_pvt;
00629 int isoutbound;
00630 struct ast_frame f = { AST_FRAME_CONTROL, AST_CONTROL_HANGUP };
00631 struct ast_channel *ochan = NULL;
00632
00633 if (!p)
00634 return -1;
00635
00636
00637
00638 ao2_ref(p, 1);
00639
00640 while (ao2_trylock(p)) {
00641 ast_channel_unlock(ast);
00642 sched_yield();
00643 ast_channel_lock(ast);
00644 }
00645
00646 isoutbound = IS_OUTBOUND(ast, p);
00647 if (isoutbound) {
00648 const char *status = pbx_builtin_getvar_helper(p->chan, "DIALSTATUS");
00649 if ((status) && (p->owner)) {
00650
00651 while (p->owner && ast_channel_trylock(p->owner)) {
00652 ao2_unlock(p);
00653 if (p->chan) {
00654 ast_channel_unlock(p->chan);
00655 }
00656 sched_yield();
00657 if (p->chan) {
00658 ast_channel_lock(p->chan);
00659 }
00660 ao2_lock(p);
00661 }
00662 if (p->owner) {
00663 p->owner->hangupcause = p->chan->hangupcause;
00664 pbx_builtin_setvar_helper(p->owner, "CHANLOCALSTATUS", status);
00665 ast_channel_unlock(p->owner);
00666 }
00667 }
00668 if (!p->chan) {
00669
00670
00671
00672
00673
00674 ao2_unlock(p);
00675 ao2_ref(p, -1);
00676 return 0;
00677 }
00678 p->chan = NULL;
00679 ast_clear_flag(p, LOCAL_LAUNCHED_PBX);
00680 ast_module_user_remove(p->u_chan);
00681 } else {
00682 ast_module_user_remove(p->u_owner);
00683 while (p->chan && ast_channel_trylock(p->chan)) {
00684 ao2_unlock(p);
00685 if (p->owner) {
00686 ast_channel_unlock(p->owner);
00687 }
00688 sched_yield();
00689 if (p->owner) {
00690 ast_channel_lock(p->owner);
00691 }
00692 ao2_lock(p);
00693 }
00694 if (p->chan) {
00695 ast_queue_hangup(p->chan);
00696 ast_channel_unlock(p->chan);
00697 }
00698
00699 if (!p->owner) {
00700
00701
00702
00703
00704
00705 ao2_unlock(p);
00706 ao2_ref(p, -1);
00707 return 0;
00708 }
00709 p->owner = NULL;
00710 }
00711
00712 ast->tech_pvt = NULL;
00713
00714 if (!p->owner && !p->chan) {
00715 ao2_unlock(p);
00716
00717
00718 ao2_unlink(locals, p);
00719 ao2_ref(p, -1);
00720 return 0;
00721 }
00722 if (p->chan && !ast_test_flag(p, LOCAL_LAUNCHED_PBX)) {
00723
00724 ochan = p->chan;
00725 } else {
00726 local_queue_frame(p, isoutbound, &f, NULL, 1);
00727 }
00728
00729 ao2_unlock(p);
00730 if (ochan) {
00731 ast_hangup(ochan);
00732 }
00733
00734 ao2_ref(p, -1);
00735 return 0;
00736 }
00737
00738
00739 static struct local_pvt *local_alloc(const char *data, int format)
00740 {
00741 struct local_pvt *tmp = NULL;
00742 char *c = NULL, *opts = NULL;
00743
00744 if (!(tmp = ao2_alloc(sizeof(*tmp), NULL))) {
00745 return NULL;
00746 }
00747
00748
00749 ast_copy_string(tmp->exten, data, sizeof(tmp->exten));
00750
00751
00752 if ((opts = strchr(tmp->exten, '/'))) {
00753 *opts++ = '\0';
00754 if (strchr(opts, 'n'))
00755 ast_set_flag(tmp, LOCAL_NO_OPTIMIZATION);
00756 if (strchr(opts, 'm'))
00757 ast_set_flag(tmp, LOCAL_MOH_PASSTHRU);
00758 }
00759
00760
00761 if ((c = strchr(tmp->exten, '@')))
00762 *c++ = '\0';
00763
00764 ast_copy_string(tmp->context, c ? c : "default", sizeof(tmp->context));
00765
00766 tmp->reqformat = format;
00767
00768 #if 0
00769
00770
00771
00772 if (!ast_exists_extension(NULL, tmp->context, tmp->exten, 1, NULL)) {
00773 ast_log(LOG_NOTICE, "No such extension/context %s@%s creating local channel\n", tmp->exten, tmp->context);
00774 tmp = local_pvt_destroy(tmp);
00775 } else {
00776 #endif
00777
00778 ao2_link(locals, tmp);
00779 #if 0
00780 }
00781 #endif
00782 return tmp;
00783 }
00784
00785
00786 static struct ast_channel *local_new(struct local_pvt *p, int state)
00787 {
00788 struct ast_channel *tmp = NULL, *tmp2 = NULL;
00789 int randnum = ast_random() & 0xffff, fmt = 0;
00790 const char *t;
00791 int ama;
00792
00793
00794
00795 if (p->owner && p->owner->accountcode)
00796 t = p->owner->accountcode;
00797 else
00798 t = "";
00799
00800 if (p->owner)
00801 ama = p->owner->amaflags;
00802 else
00803 ama = 0;
00804 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))
00805 || !(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))) {
00806 if (tmp)
00807 ast_channel_free(tmp);
00808 if (tmp2)
00809 ast_channel_free(tmp2);
00810 ast_log(LOG_WARNING, "Unable to allocate channel structure(s)\n");
00811 return NULL;
00812 }
00813
00814 tmp2->tech = tmp->tech = &local_tech;
00815
00816 tmp->nativeformats = p->reqformat;
00817 tmp2->nativeformats = p->reqformat;
00818
00819
00820 fmt = ast_best_codec(p->reqformat);
00821 tmp->writeformat = fmt;
00822 tmp2->writeformat = fmt;
00823 tmp->rawwriteformat = fmt;
00824 tmp2->rawwriteformat = fmt;
00825 tmp->readformat = fmt;
00826 tmp2->readformat = fmt;
00827 tmp->rawreadformat = fmt;
00828 tmp2->rawreadformat = fmt;
00829
00830 tmp->tech_pvt = p;
00831 tmp2->tech_pvt = p;
00832
00833 p->owner = tmp;
00834 p->chan = tmp2;
00835 p->u_owner = ast_module_user_add(p->owner);
00836 p->u_chan = ast_module_user_add(p->chan);
00837
00838 ast_copy_string(tmp->context, p->context, sizeof(tmp->context));
00839 ast_copy_string(tmp2->context, p->context, sizeof(tmp2->context));
00840 ast_copy_string(tmp2->exten, p->exten, sizeof(tmp->exten));
00841 tmp->priority = 1;
00842 tmp2->priority = 1;
00843
00844 return tmp;
00845 }
00846
00847
00848 static struct ast_channel *local_request(const char *type, int format, void *data, int *cause)
00849 {
00850 struct local_pvt *p = NULL;
00851 struct ast_channel *chan = NULL;
00852
00853
00854 if ((p = local_alloc(data, format))) {
00855 if (!(chan = local_new(p, AST_STATE_DOWN))) {
00856 ao2_unlink(locals, p);
00857 }
00858 ao2_ref(p, -1);
00859 }
00860
00861 return chan;
00862 }
00863
00864
00865 static int locals_show(int fd, int argc, char **argv)
00866 {
00867 struct local_pvt *p = NULL;
00868 struct ao2_iterator it;
00869
00870 if (argc != 3)
00871 return RESULT_SHOWUSAGE;
00872
00873
00874 if (ao2_container_count(locals) == 0) {
00875 ast_cli(fd, "No local channels in use\n");
00876 return RESULT_SUCCESS;
00877 }
00878
00879 it = ao2_iterator_init(locals, 0);
00880 while ((p = ao2_iterator_next(&it))) {
00881 ao2_lock(p);
00882 ast_cli(fd, "%s -- %s@%s\n", p->owner ? p->owner->name : "<unowned>", p->exten, p->context);
00883 ao2_unlock(p);
00884 ao2_ref(p, -1);
00885 }
00886 ao2_iterator_destroy(&it);
00887
00888 return RESULT_SUCCESS;
00889 }
00890
00891 static char show_locals_usage[] =
00892 "Usage: local show channels\n"
00893 " Provides summary information on active local proxy channels.\n";
00894
00895 static struct ast_cli_entry cli_local[] = {
00896 { { "local", "show", "channels", NULL },
00897 locals_show, "List status of local channels",
00898 show_locals_usage },
00899 };
00900
00901 static int locals_cmp_cb(void *obj, void *arg, int flags)
00902 {
00903 return (obj == arg) ? CMP_MATCH : 0;
00904 }
00905
00906
00907 static int load_module(void)
00908 {
00909 if (!(locals = ao2_container_alloc(BUCKET_SIZE, NULL, locals_cmp_cb))) {
00910 return -1;
00911 }
00912
00913
00914 if (ast_channel_register(&local_tech)) {
00915 ast_log(LOG_ERROR, "Unable to register channel class 'Local'\n");
00916 ao2_ref(locals, -1);
00917 return -1;
00918 }
00919
00920 ast_cli_register_multiple(cli_local, sizeof(cli_local) / sizeof(struct ast_cli_entry));
00921 return 0;
00922 }
00923
00924
00925 static int unload_module(void)
00926 {
00927 struct local_pvt *p = NULL;
00928 struct ao2_iterator it;
00929
00930
00931 ast_cli_unregister_multiple(cli_local, sizeof(cli_local) / sizeof(struct ast_cli_entry));
00932 ast_channel_unregister(&local_tech);
00933
00934 it = ao2_iterator_init(locals, 0);
00935 while ((p = ao2_iterator_next(&it))) {
00936 if (p->owner) {
00937 ast_softhangup(p->owner, AST_SOFTHANGUP_APPUNLOAD);
00938 }
00939 ao2_ref(p, -1);
00940 }
00941 ao2_iterator_destroy(&it);
00942 ao2_ref(locals, -1);
00943 return 0;
00944 }
00945
00946 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Local Proxy Channel (Note: used internally by other modules)");