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
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054 #include "asterisk.h"
00055
00056 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 249896 $")
00057
00058 #include <sys/signal.h>
00059
00060 #include <portaudio.h>
00061
00062 #include "asterisk/module.h"
00063 #include "asterisk/channel.h"
00064 #include "asterisk/pbx.h"
00065 #include "asterisk/causes.h"
00066 #include "asterisk/cli.h"
00067 #include "asterisk/musiconhold.h"
00068 #include "asterisk/callerid.h"
00069 #include "asterisk/astobj2.h"
00070
00071
00072
00073
00074
00075
00076
00077 #define SAMPLE_RATE 16000
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089 #define NUM_SAMPLES 320
00090
00091
00092 #define INPUT_CHANNELS 1
00093
00094
00095 #define OUTPUT_CHANNELS 1
00096
00097
00098
00099
00100
00101
00102 #define TEXT_SIZE 256
00103
00104
00105 #define V_BEGIN " --- <(\"<) --- "
00106 #define V_END " --- (>\")> ---\n"
00107
00108
00109 static const char config_file[] = "console.conf";
00110
00111
00112
00113
00114
00115
00116
00117 static struct console_pvt {
00118 AST_DECLARE_STRING_FIELDS(
00119
00120 AST_STRING_FIELD(name);
00121 AST_STRING_FIELD(input_device);
00122 AST_STRING_FIELD(output_device);
00123
00124 AST_STRING_FIELD(context);
00125
00126 AST_STRING_FIELD(exten);
00127
00128 AST_STRING_FIELD(cid_num);
00129
00130 AST_STRING_FIELD(cid_name);
00131
00132
00133
00134 AST_STRING_FIELD(mohinterpret);
00135
00136 AST_STRING_FIELD(language);
00137
00138 AST_STRING_FIELD(parkinglot);
00139 );
00140
00141 struct ast_channel *owner;
00142
00143 PaStream *stream;
00144
00145 struct ast_frame fr;
00146
00147 unsigned int streamstate:1;
00148
00149 unsigned int hookstate:1;
00150
00151 unsigned int muted:1;
00152
00153 unsigned int autoanswer:1;
00154
00155 unsigned int overridecontext:1;
00156
00157
00158 unsigned int destroy:1;
00159
00160 pthread_t thread;
00161 } globals;
00162
00163 AST_MUTEX_DEFINE_STATIC(globals_lock);
00164
00165 static struct ao2_container *pvts;
00166 #define NUM_PVT_BUCKETS 7
00167
00168 static struct console_pvt *active_pvt;
00169 AST_RWLOCK_DEFINE_STATIC(active_lock);
00170
00171
00172
00173
00174
00175
00176 static struct ast_jb_conf default_jbconf = {
00177 .flags = 0,
00178 .max_size = -1,
00179 .resync_threshold = -1,
00180 .impl = "",
00181 .target_extra = -1,
00182 };
00183 static struct ast_jb_conf global_jbconf;
00184
00185
00186 static struct ast_channel *console_request(const char *type, int format,
00187 void *data, int *cause);
00188 static int console_digit_begin(struct ast_channel *c, char digit);
00189 static int console_digit_end(struct ast_channel *c, char digit, unsigned int duration);
00190 static int console_text(struct ast_channel *c, const char *text);
00191 static int console_hangup(struct ast_channel *c);
00192 static int console_answer(struct ast_channel *c);
00193 static struct ast_frame *console_read(struct ast_channel *chan);
00194 static int console_call(struct ast_channel *c, char *dest, int timeout);
00195 static int console_write(struct ast_channel *chan, struct ast_frame *f);
00196 static int console_indicate(struct ast_channel *chan, int cond,
00197 const void *data, size_t datalen);
00198 static int console_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
00199
00200
00201
00202
00203
00204 #define SUPPORTED_FORMATS ( AST_FORMAT_SLINEAR16 )
00205
00206 static const struct ast_channel_tech console_tech = {
00207 .type = "Console",
00208 .description = "Console Channel Driver",
00209 .capabilities = SUPPORTED_FORMATS,
00210 .requester = console_request,
00211 .send_digit_begin = console_digit_begin,
00212 .send_digit_end = console_digit_end,
00213 .send_text = console_text,
00214 .hangup = console_hangup,
00215 .answer = console_answer,
00216 .read = console_read,
00217 .call = console_call,
00218 .write = console_write,
00219 .indicate = console_indicate,
00220 .fixup = console_fixup,
00221 };
00222
00223
00224 #define console_pvt_lock(pvt) ao2_lock(pvt)
00225
00226
00227 #define console_pvt_unlock(pvt) ao2_unlock(pvt)
00228
00229 static inline struct console_pvt *ref_pvt(struct console_pvt *pvt)
00230 {
00231 if (pvt)
00232 ao2_ref(pvt, +1);
00233 return pvt;
00234 }
00235
00236 static inline struct console_pvt *unref_pvt(struct console_pvt *pvt)
00237 {
00238 ao2_ref(pvt, -1);
00239 return NULL;
00240 }
00241
00242 static struct console_pvt *find_pvt(const char *name)
00243 {
00244 struct console_pvt tmp_pvt = {
00245 .name = name,
00246 };
00247
00248 return ao2_find(pvts, &tmp_pvt, OBJ_POINTER);
00249 }
00250
00251
00252
00253
00254
00255
00256
00257
00258
00259
00260
00261 static void *stream_monitor(void *data)
00262 {
00263 struct console_pvt *pvt = data;
00264 char buf[NUM_SAMPLES * sizeof(int16_t)];
00265 PaError res;
00266 struct ast_frame f = {
00267 .frametype = AST_FRAME_VOICE,
00268 .subclass = AST_FORMAT_SLINEAR16,
00269 .src = "console_stream_monitor",
00270 .data.ptr = buf,
00271 .datalen = sizeof(buf),
00272 .samples = sizeof(buf) / sizeof(int16_t),
00273 };
00274
00275 for (;;) {
00276 pthread_testcancel();
00277 res = Pa_ReadStream(pvt->stream, buf, sizeof(buf) / sizeof(int16_t));
00278 pthread_testcancel();
00279
00280 if (res == paNoError)
00281 ast_queue_frame(pvt->owner, &f);
00282 }
00283
00284 return NULL;
00285 }
00286
00287 static int open_stream(struct console_pvt *pvt)
00288 {
00289 int res = paInternalError;
00290
00291 if (!strcasecmp(pvt->input_device, "default") &&
00292 !strcasecmp(pvt->output_device, "default")) {
00293 res = Pa_OpenDefaultStream(&pvt->stream, INPUT_CHANNELS, OUTPUT_CHANNELS,
00294 paInt16, SAMPLE_RATE, NUM_SAMPLES, NULL, NULL);
00295 } else {
00296 PaStreamParameters input_params = {
00297 .channelCount = 1,
00298 .sampleFormat = paInt16,
00299 .suggestedLatency = (1.0 / 50.0),
00300 .device = paNoDevice,
00301 };
00302 PaStreamParameters output_params = {
00303 .channelCount = 1,
00304 .sampleFormat = paInt16,
00305 .suggestedLatency = (1.0 / 50.0),
00306 .device = paNoDevice,
00307 };
00308 PaDeviceIndex idx, num_devices, def_input, def_output;
00309
00310 if (!(num_devices = Pa_GetDeviceCount()))
00311 return res;
00312
00313 def_input = Pa_GetDefaultInputDevice();
00314 def_output = Pa_GetDefaultOutputDevice();
00315
00316 for (idx = 0;
00317 idx < num_devices && (input_params.device == paNoDevice
00318 || output_params.device == paNoDevice);
00319 idx++)
00320 {
00321 const PaDeviceInfo *dev = Pa_GetDeviceInfo(idx);
00322
00323 if (dev->maxInputChannels) {
00324 if ( (idx == def_input && !strcasecmp(pvt->input_device, "default")) ||
00325 !strcasecmp(pvt->input_device, dev->name) )
00326 input_params.device = idx;
00327 }
00328
00329 if (dev->maxOutputChannels) {
00330 if ( (idx == def_output && !strcasecmp(pvt->output_device, "default")) ||
00331 !strcasecmp(pvt->output_device, dev->name) )
00332 output_params.device = idx;
00333 }
00334 }
00335
00336 if (input_params.device == paNoDevice)
00337 ast_log(LOG_ERROR, "No input device found for console device '%s'\n", pvt->name);
00338 if (output_params.device == paNoDevice)
00339 ast_log(LOG_ERROR, "No output device found for console device '%s'\n", pvt->name);
00340
00341 res = Pa_OpenStream(&pvt->stream, &input_params, &output_params,
00342 SAMPLE_RATE, NUM_SAMPLES, paNoFlag, NULL, NULL);
00343 }
00344
00345 return res;
00346 }
00347
00348 static int start_stream(struct console_pvt *pvt)
00349 {
00350 PaError res;
00351 int ret_val = 0;
00352
00353 console_pvt_lock(pvt);
00354
00355 if (pvt->streamstate)
00356 goto return_unlock;
00357
00358 pvt->streamstate = 1;
00359 ast_debug(1, "Starting stream\n");
00360
00361 res = open_stream(pvt);
00362 if (res != paNoError) {
00363 ast_log(LOG_WARNING, "Failed to open stream - (%d) %s\n",
00364 res, Pa_GetErrorText(res));
00365 ret_val = -1;
00366 goto return_unlock;
00367 }
00368
00369 res = Pa_StartStream(pvt->stream);
00370 if (res != paNoError) {
00371 ast_log(LOG_WARNING, "Failed to start stream - (%d) %s\n",
00372 res, Pa_GetErrorText(res));
00373 ret_val = -1;
00374 goto return_unlock;
00375 }
00376
00377 if (ast_pthread_create_background(&pvt->thread, NULL, stream_monitor, pvt)) {
00378 ast_log(LOG_ERROR, "Failed to start stream monitor thread\n");
00379 ret_val = -1;
00380 }
00381
00382 return_unlock:
00383 console_pvt_unlock(pvt);
00384
00385 return ret_val;
00386 }
00387
00388 static int stop_stream(struct console_pvt *pvt)
00389 {
00390 if (!pvt->streamstate || pvt->thread == AST_PTHREADT_NULL)
00391 return 0;
00392
00393 pthread_cancel(pvt->thread);
00394 pthread_kill(pvt->thread, SIGURG);
00395 pthread_join(pvt->thread, NULL);
00396
00397 console_pvt_lock(pvt);
00398 Pa_AbortStream(pvt->stream);
00399 Pa_CloseStream(pvt->stream);
00400 pvt->stream = NULL;
00401 pvt->streamstate = 0;
00402 console_pvt_unlock(pvt);
00403
00404 return 0;
00405 }
00406
00407
00408
00409
00410 static struct ast_channel *console_new(struct console_pvt *pvt, const char *ext, const char *ctx, int state)
00411 {
00412 struct ast_channel *chan;
00413
00414 if (!(chan = ast_channel_alloc(1, state, pvt->cid_num, pvt->cid_name, NULL,
00415 ext, ctx, 0, "Console/%s", pvt->name))) {
00416 return NULL;
00417 }
00418
00419 chan->tech = &console_tech;
00420 chan->nativeformats = AST_FORMAT_SLINEAR16;
00421 chan->readformat = AST_FORMAT_SLINEAR16;
00422 chan->writeformat = AST_FORMAT_SLINEAR16;
00423 chan->tech_pvt = ref_pvt(pvt);
00424
00425 pvt->owner = chan;
00426
00427 if (!ast_strlen_zero(pvt->language))
00428 ast_string_field_set(chan, language, pvt->language);
00429
00430 ast_jb_configure(chan, &global_jbconf);
00431
00432 if (state != AST_STATE_DOWN) {
00433 if (ast_pbx_start(chan)) {
00434 chan->hangupcause = AST_CAUSE_SWITCH_CONGESTION;
00435 ast_hangup(chan);
00436 chan = NULL;
00437 } else
00438 start_stream(pvt);
00439 }
00440
00441 return chan;
00442 }
00443
00444 static struct ast_channel *console_request(const char *type, int format, void *data, int *cause)
00445 {
00446 int oldformat = format;
00447 struct ast_channel *chan = NULL;
00448 struct console_pvt *pvt;
00449
00450 if (!(pvt = find_pvt(data))) {
00451 ast_log(LOG_ERROR, "Console device '%s' not found\n", (char *) data);
00452 return NULL;
00453 }
00454
00455 format &= SUPPORTED_FORMATS;
00456 if (!format) {
00457 ast_log(LOG_NOTICE, "Channel requested with unsupported format(s): '%d'\n", oldformat);
00458 goto return_unref;
00459 }
00460
00461 if (pvt->owner) {
00462 ast_log(LOG_NOTICE, "Console channel already active!\n");
00463 *cause = AST_CAUSE_BUSY;
00464 goto return_unref;
00465 }
00466
00467 console_pvt_lock(pvt);
00468 chan = console_new(pvt, NULL, NULL, AST_STATE_DOWN);
00469 console_pvt_unlock(pvt);
00470
00471 if (!chan)
00472 ast_log(LOG_WARNING, "Unable to create new Console channel!\n");
00473
00474 return_unref:
00475 unref_pvt(pvt);
00476
00477 return chan;
00478 }
00479
00480 static int console_digit_begin(struct ast_channel *c, char digit)
00481 {
00482 ast_verb(1, V_BEGIN "Console Received Beginning of Digit %c" V_END, digit);
00483
00484 return -1;
00485 }
00486
00487 static int console_digit_end(struct ast_channel *c, char digit, unsigned int duration)
00488 {
00489 ast_verb(1, V_BEGIN "Console Received End of Digit %c (duration %u)" V_END,
00490 digit, duration);
00491
00492 return -1;
00493 }
00494
00495 static int console_text(struct ast_channel *c, const char *text)
00496 {
00497 ast_verb(1, V_BEGIN "Console Received Text '%s'" V_END, text);
00498
00499 return 0;
00500 }
00501
00502 static int console_hangup(struct ast_channel *c)
00503 {
00504 struct console_pvt *pvt = c->tech_pvt;
00505
00506 ast_verb(1, V_BEGIN "Hangup on Console" V_END);
00507
00508 pvt->hookstate = 0;
00509 pvt->owner = NULL;
00510 stop_stream(pvt);
00511
00512 c->tech_pvt = unref_pvt(pvt);
00513
00514 return 0;
00515 }
00516
00517 static int console_answer(struct ast_channel *c)
00518 {
00519 struct console_pvt *pvt = c->tech_pvt;
00520
00521 ast_verb(1, V_BEGIN "Call from Console has been Answered" V_END);
00522
00523 ast_setstate(c, AST_STATE_UP);
00524
00525 return start_stream(pvt);
00526 }
00527
00528
00529
00530
00531
00532
00533
00534
00535
00536
00537
00538
00539
00540
00541
00542
00543
00544
00545
00546
00547
00548 static struct ast_frame *console_read(struct ast_channel *chan)
00549 {
00550 ast_debug(1, "I should not be called ...\n");
00551
00552 return &ast_null_frame;
00553 }
00554
00555 static int console_call(struct ast_channel *c, char *dest, int timeout)
00556 {
00557 struct ast_frame f = { 0, };
00558 struct console_pvt *pvt = c->tech_pvt;
00559
00560 ast_verb(1, V_BEGIN "Call to device '%s' on console from '%s' <%s>" V_END,
00561 dest, c->cid.cid_name, c->cid.cid_num);
00562
00563 console_pvt_lock(pvt);
00564
00565 if (pvt->autoanswer) {
00566 pvt->hookstate = 1;
00567 console_pvt_unlock(pvt);
00568 ast_verb(1, V_BEGIN "Auto-answered" V_END);
00569 f.frametype = AST_FRAME_CONTROL;
00570 f.subclass = AST_CONTROL_ANSWER;
00571 } else {
00572 console_pvt_unlock(pvt);
00573 ast_verb(1, V_BEGIN "Type 'console answer' to answer, or use the 'autoanswer' option "
00574 "for future calls" V_END);
00575 f.frametype = AST_FRAME_CONTROL;
00576 f.subclass = AST_CONTROL_RINGING;
00577 ast_indicate(c, AST_CONTROL_RINGING);
00578 }
00579
00580 ast_queue_frame(c, &f);
00581
00582 return start_stream(pvt);
00583 }
00584
00585 static int console_write(struct ast_channel *chan, struct ast_frame *f)
00586 {
00587 struct console_pvt *pvt = chan->tech_pvt;
00588
00589 Pa_WriteStream(pvt->stream, f->data.ptr, f->samples);
00590
00591 return 0;
00592 }
00593
00594 static int console_indicate(struct ast_channel *chan, int cond, const void *data, size_t datalen)
00595 {
00596 struct console_pvt *pvt = chan->tech_pvt;
00597 int res = 0;
00598
00599 switch (cond) {
00600 case AST_CONTROL_BUSY:
00601 case AST_CONTROL_CONGESTION:
00602 case AST_CONTROL_RINGING:
00603 case -1:
00604 res = -1;
00605 break;
00606 case AST_CONTROL_PROGRESS:
00607 case AST_CONTROL_PROCEEDING:
00608 case AST_CONTROL_VIDUPDATE:
00609 case AST_CONTROL_SRCUPDATE:
00610 break;
00611 case AST_CONTROL_HOLD:
00612 ast_verb(1, V_BEGIN "Console Has Been Placed on Hold" V_END);
00613 ast_moh_start(chan, data, pvt->mohinterpret);
00614 break;
00615 case AST_CONTROL_UNHOLD:
00616 ast_verb(1, V_BEGIN "Console Has Been Retrieved from Hold" V_END);
00617 ast_moh_stop(chan);
00618 break;
00619 default:
00620 ast_log(LOG_WARNING, "Don't know how to display condition %d on %s\n",
00621 cond, chan->name);
00622
00623 res = -1;
00624 }
00625
00626 return res;
00627 }
00628
00629 static int console_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
00630 {
00631 struct console_pvt *pvt = newchan->tech_pvt;
00632
00633 pvt->owner = newchan;
00634
00635 return 0;
00636 }
00637
00638
00639
00640
00641
00642
00643
00644
00645
00646
00647
00648
00649 static char *ast_ext_ctx(struct console_pvt *pvt, const char *src, char **ext, char **ctx)
00650 {
00651 if (ext == NULL || ctx == NULL)
00652 return NULL;
00653
00654 *ext = *ctx = NULL;
00655
00656 if (src && *src != '\0')
00657 *ext = ast_strdup(src);
00658
00659 if (*ext == NULL)
00660 return NULL;
00661
00662 if (!pvt->overridecontext) {
00663
00664 *ctx = strrchr(*ext, '@');
00665 if (*ctx)
00666 *(*ctx)++ = '\0';
00667 }
00668
00669 return *ext;
00670 }
00671
00672 static struct console_pvt *get_active_pvt(void)
00673 {
00674 struct console_pvt *pvt;
00675
00676 ast_rwlock_rdlock(&active_lock);
00677 pvt = ref_pvt(active_pvt);
00678 ast_rwlock_unlock(&active_lock);
00679
00680 return pvt;
00681 }
00682
00683 static char *cli_console_autoanswer(struct ast_cli_entry *e, int cmd,
00684 struct ast_cli_args *a)
00685 {
00686 struct console_pvt *pvt = get_active_pvt();
00687 char *res = CLI_SUCCESS;
00688
00689 switch (cmd) {
00690 case CLI_INIT:
00691 e->command = "console set autoanswer [on|off]";
00692 e->usage =
00693 "Usage: console set autoanswer [on|off]\n"
00694 " Enables or disables autoanswer feature. If used without\n"
00695 " argument, displays the current on/off status of autoanswer.\n"
00696 " The default value of autoanswer is in 'oss.conf'.\n";
00697 return NULL;
00698
00699 case CLI_GENERATE:
00700 return NULL;
00701 }
00702
00703 if (!pvt) {
00704 ast_cli(a->fd, "No console device is set as active.\n");
00705 return CLI_FAILURE;
00706 }
00707
00708 if (a->argc == e->args - 1) {
00709 ast_cli(a->fd, "Auto answer is %s.\n", pvt->autoanswer ? "on" : "off");
00710 unref_pvt(pvt);
00711 return CLI_SUCCESS;
00712 }
00713
00714 if (a->argc != e->args) {
00715 unref_pvt(pvt);
00716 return CLI_SHOWUSAGE;
00717 }
00718
00719 if (!strcasecmp(a->argv[e->args-1], "on"))
00720 pvt->autoanswer = 1;
00721 else if (!strcasecmp(a->argv[e->args - 1], "off"))
00722 pvt->autoanswer = 0;
00723 else
00724 res = CLI_SHOWUSAGE;
00725
00726 unref_pvt(pvt);
00727
00728 return CLI_SUCCESS;
00729 }
00730
00731 static char *cli_console_flash(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00732 {
00733 struct ast_frame f = { AST_FRAME_CONTROL, AST_CONTROL_FLASH };
00734 struct console_pvt *pvt = get_active_pvt();
00735
00736 if (cmd == CLI_INIT) {
00737 e->command = "console flash";
00738 e->usage =
00739 "Usage: console flash\n"
00740 " Flashes the call currently placed on the console.\n";
00741 return NULL;
00742 } else if (cmd == CLI_GENERATE)
00743 return NULL;
00744
00745 if (!pvt) {
00746 ast_cli(a->fd, "No console device is set as active\n");
00747 return CLI_FAILURE;
00748 }
00749
00750 if (a->argc != e->args)
00751 return CLI_SHOWUSAGE;
00752
00753 if (!pvt->owner) {
00754 ast_cli(a->fd, "No call to flash\n");
00755 unref_pvt(pvt);
00756 return CLI_FAILURE;
00757 }
00758
00759 pvt->hookstate = 0;
00760
00761 ast_queue_frame(pvt->owner, &f);
00762
00763 unref_pvt(pvt);
00764
00765 return CLI_SUCCESS;
00766 }
00767
00768 static char *cli_console_dial(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00769 {
00770 char *s = NULL;
00771 const char *mye = NULL, *myc = NULL;
00772 struct console_pvt *pvt = get_active_pvt();
00773
00774 if (cmd == CLI_INIT) {
00775 e->command = "console dial";
00776 e->usage =
00777 "Usage: console dial [extension[@context]]\n"
00778 " Dials a given extension (and context if specified)\n";
00779 return NULL;
00780 } else if (cmd == CLI_GENERATE)
00781 return NULL;
00782
00783 if (!pvt) {
00784 ast_cli(a->fd, "No console device is currently set as active\n");
00785 return CLI_FAILURE;
00786 }
00787
00788 if (a->argc > e->args + 1)
00789 return CLI_SHOWUSAGE;
00790
00791 if (pvt->owner) {
00792 int i;
00793 struct ast_frame f = { AST_FRAME_DTMF, 0 };
00794
00795 if (a->argc == e->args) {
00796 ast_cli(a->fd, "Already in a call. You can only dial digits until you hangup.\n");
00797 unref_pvt(pvt);
00798 return CLI_FAILURE;
00799 }
00800 s = a->argv[e->args];
00801
00802 for (i = 0; i < strlen(s); i++) {
00803 f.subclass = s[i];
00804 ast_queue_frame(pvt->owner, &f);
00805 }
00806 unref_pvt(pvt);
00807 return CLI_SUCCESS;
00808 }
00809
00810
00811 if (a->argc == e->args + 1) {
00812 char *ext = NULL, *con = NULL;
00813 s = ast_ext_ctx(pvt, a->argv[e->args], &ext, &con);
00814 ast_debug(1, "provided '%s', exten '%s' context '%s'\n",
00815 a->argv[e->args], mye, myc);
00816 mye = ext;
00817 myc = con;
00818 }
00819
00820
00821 if (ast_strlen_zero(mye))
00822 mye = pvt->exten;
00823 if (ast_strlen_zero(myc))
00824 myc = pvt->context;
00825
00826 if (ast_exists_extension(NULL, myc, mye, 1, NULL)) {
00827 console_pvt_lock(pvt);
00828 pvt->hookstate = 1;
00829 console_new(pvt, mye, myc, AST_STATE_RINGING);
00830 console_pvt_unlock(pvt);
00831 } else
00832 ast_cli(a->fd, "No such extension '%s' in context '%s'\n", mye, myc);
00833
00834 if (s)
00835 free(s);
00836
00837 unref_pvt(pvt);
00838
00839 return CLI_SUCCESS;
00840 }
00841
00842 static char *cli_console_hangup(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00843 {
00844 struct console_pvt *pvt = get_active_pvt();
00845
00846 if (cmd == CLI_INIT) {
00847 e->command = "console hangup";
00848 e->usage =
00849 "Usage: console hangup\n"
00850 " Hangs up any call currently placed on the console.\n";
00851 return NULL;
00852 } else if (cmd == CLI_GENERATE)
00853 return NULL;
00854
00855 if (!pvt) {
00856 ast_cli(a->fd, "No console device is set as active\n");
00857 return CLI_FAILURE;
00858 }
00859
00860 if (a->argc != e->args)
00861 return CLI_SHOWUSAGE;
00862
00863 if (!pvt->owner && !pvt->hookstate) {
00864 ast_cli(a->fd, "No call to hang up\n");
00865 unref_pvt(pvt);
00866 return CLI_FAILURE;
00867 }
00868
00869 pvt->hookstate = 0;
00870 if (pvt->owner)
00871 ast_queue_hangup(pvt->owner);
00872
00873 unref_pvt(pvt);
00874
00875 return CLI_SUCCESS;
00876 }
00877
00878 static char *cli_console_mute(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00879 {
00880 char *s;
00881 struct console_pvt *pvt = get_active_pvt();
00882 char *res = CLI_SUCCESS;
00883
00884 if (cmd == CLI_INIT) {
00885 e->command = "console {mute|unmute}";
00886 e->usage =
00887 "Usage: console {mute|unmute}\n"
00888 " Mute/unmute the microphone.\n";
00889 return NULL;
00890 } else if (cmd == CLI_GENERATE)
00891 return NULL;
00892
00893 if (!pvt) {
00894 ast_cli(a->fd, "No console device is set as active\n");
00895 return CLI_FAILURE;
00896 }
00897
00898 if (a->argc != e->args)
00899 return CLI_SHOWUSAGE;
00900
00901 s = a->argv[e->args-1];
00902 if (!strcasecmp(s, "mute"))
00903 pvt->muted = 1;
00904 else if (!strcasecmp(s, "unmute"))
00905 pvt->muted = 0;
00906 else
00907 res = CLI_SHOWUSAGE;
00908
00909 ast_verb(1, V_BEGIN "The Console is now %s" V_END,
00910 pvt->muted ? "Muted" : "Unmuted");
00911
00912 unref_pvt(pvt);
00913
00914 return res;
00915 }
00916
00917 static char *cli_list_available(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00918 {
00919 PaDeviceIndex idx, num, def_input, def_output;
00920
00921 if (cmd == CLI_INIT) {
00922 e->command = "console list available";
00923 e->usage =
00924 "Usage: console list available\n"
00925 " List all available devices.\n";
00926 return NULL;
00927 } else if (cmd == CLI_GENERATE)
00928 return NULL;
00929
00930 if (a->argc != e->args)
00931 return CLI_SHOWUSAGE;
00932
00933 ast_cli(a->fd, "\n"
00934 "=============================================================\n"
00935 "=== Available Devices =======================================\n"
00936 "=============================================================\n"
00937 "===\n");
00938
00939 num = Pa_GetDeviceCount();
00940 if (!num) {
00941 ast_cli(a->fd, "(None)\n");
00942 return CLI_SUCCESS;
00943 }
00944
00945 def_input = Pa_GetDefaultInputDevice();
00946 def_output = Pa_GetDefaultOutputDevice();
00947 for (idx = 0; idx < num; idx++) {
00948 const PaDeviceInfo *dev = Pa_GetDeviceInfo(idx);
00949 if (!dev)
00950 continue;
00951 ast_cli(a->fd, "=== ---------------------------------------------------------\n"
00952 "=== Device Name: %s\n", dev->name);
00953 if (dev->maxInputChannels)
00954 ast_cli(a->fd, "=== ---> %sInput Device\n", (idx == def_input) ? "Default " : "");
00955 if (dev->maxOutputChannels)
00956 ast_cli(a->fd, "=== ---> %sOutput Device\n", (idx == def_output) ? "Default " : "");
00957 ast_cli(a->fd, "=== ---------------------------------------------------------\n===\n");
00958 }
00959
00960 ast_cli(a->fd, "=============================================================\n\n");
00961
00962 return CLI_SUCCESS;
00963 }
00964
00965 static char *cli_list_devices(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00966 {
00967 struct ao2_iterator i;
00968 struct console_pvt *pvt;
00969
00970 if (cmd == CLI_INIT) {
00971 e->command = "console list devices";
00972 e->usage =
00973 "Usage: console list devices\n"
00974 " List all configured devices.\n";
00975 return NULL;
00976 } else if (cmd == CLI_GENERATE)
00977 return NULL;
00978
00979 if (a->argc != e->args)
00980 return CLI_SHOWUSAGE;
00981
00982 ast_cli(a->fd, "\n"
00983 "=============================================================\n"
00984 "=== Configured Devices ======================================\n"
00985 "=============================================================\n"
00986 "===\n");
00987
00988 i = ao2_iterator_init(pvts, 0);
00989 while ((pvt = ao2_iterator_next(&i))) {
00990 console_pvt_lock(pvt);
00991
00992 ast_cli(a->fd, "=== ---------------------------------------------------------\n"
00993 "=== Device Name: %s\n"
00994 "=== ---> Active: %s\n"
00995 "=== ---> Input Device: %s\n"
00996 "=== ---> Output Device: %s\n"
00997 "=== ---> Context: %s\n"
00998 "=== ---> Extension: %s\n"
00999 "=== ---> CallerID Num: %s\n"
01000 "=== ---> CallerID Name: %s\n"
01001 "=== ---> MOH Interpret: %s\n"
01002 "=== ---> Language: %s\n"
01003 "=== ---> Parkinglot: %s\n"
01004 "=== ---> Muted: %s\n"
01005 "=== ---> Auto-Answer: %s\n"
01006 "=== ---> Override Context: %s\n"
01007 "=== ---------------------------------------------------------\n===\n",
01008 pvt->name, (pvt == active_pvt) ? "Yes" : "No",
01009 pvt->input_device, pvt->output_device, pvt->context,
01010 pvt->exten, pvt->cid_num, pvt->cid_name, pvt->mohinterpret,
01011 pvt->language, pvt->parkinglot, pvt->muted ? "Yes" : "No", pvt->autoanswer ? "Yes" : "No",
01012 pvt->overridecontext ? "Yes" : "No");
01013
01014 console_pvt_unlock(pvt);
01015 unref_pvt(pvt);
01016 }
01017 ao2_iterator_destroy(&i);
01018
01019 ast_cli(a->fd, "=============================================================\n\n");
01020
01021 return CLI_SUCCESS;
01022 }
01023
01024
01025
01026 static char *cli_console_answer(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01027 {
01028 struct ast_frame f = { AST_FRAME_CONTROL, AST_CONTROL_ANSWER };
01029 struct console_pvt *pvt = get_active_pvt();
01030
01031 switch (cmd) {
01032 case CLI_INIT:
01033 e->command = "console answer";
01034 e->usage =
01035 "Usage: console answer\n"
01036 " Answers an incoming call on the console channel.\n";
01037 return NULL;
01038
01039 case CLI_GENERATE:
01040 return NULL;
01041 }
01042
01043 if (!pvt) {
01044 ast_cli(a->fd, "No console device is set as active\n");
01045 return CLI_FAILURE;
01046 }
01047
01048 if (a->argc != e->args) {
01049 unref_pvt(pvt);
01050 return CLI_SHOWUSAGE;
01051 }
01052
01053 if (!pvt->owner) {
01054 ast_cli(a->fd, "No one is calling us\n");
01055 unref_pvt(pvt);
01056 return CLI_FAILURE;
01057 }
01058
01059 pvt->hookstate = 1;
01060
01061 ast_indicate(pvt->owner, -1);
01062
01063 ast_queue_frame(pvt->owner, &f);
01064
01065 unref_pvt(pvt);
01066
01067 return CLI_SUCCESS;
01068 }
01069
01070
01071
01072
01073
01074
01075
01076 static char *cli_console_sendtext(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01077 {
01078 char buf[TEXT_SIZE];
01079 struct console_pvt *pvt = get_active_pvt();
01080 struct ast_frame f = {
01081 .frametype = AST_FRAME_TEXT,
01082 .data.ptr = buf,
01083 .src = "console_send_text",
01084 };
01085 int len;
01086
01087 if (cmd == CLI_INIT) {
01088 e->command = "console send text";
01089 e->usage =
01090 "Usage: console send text <message>\n"
01091 " Sends a text message for display on the remote terminal.\n";
01092 return NULL;
01093 } else if (cmd == CLI_GENERATE)
01094 return NULL;
01095
01096 if (!pvt) {
01097 ast_cli(a->fd, "No console device is set as active\n");
01098 return CLI_FAILURE;
01099 }
01100
01101 if (a->argc < e->args + 1) {
01102 unref_pvt(pvt);
01103 return CLI_SHOWUSAGE;
01104 }
01105
01106 if (!pvt->owner) {
01107 ast_cli(a->fd, "Not in a call\n");
01108 unref_pvt(pvt);
01109 return CLI_FAILURE;
01110 }
01111
01112 ast_join(buf, sizeof(buf) - 1, a->argv + e->args);
01113 if (ast_strlen_zero(buf)) {
01114 unref_pvt(pvt);
01115 return CLI_SHOWUSAGE;
01116 }
01117
01118 len = strlen(buf);
01119 buf[len] = '\n';
01120 f.datalen = len + 1;
01121
01122 ast_queue_frame(pvt->owner, &f);
01123
01124 unref_pvt(pvt);
01125
01126 return CLI_SUCCESS;
01127 }
01128
01129 static void set_active(struct console_pvt *pvt, const char *value)
01130 {
01131 if (pvt == &globals) {
01132 ast_log(LOG_ERROR, "active is only valid as a per-device setting\n");
01133 return;
01134 }
01135
01136 if (!ast_true(value))
01137 return;
01138
01139 ast_rwlock_wrlock(&active_lock);
01140 if (active_pvt)
01141 unref_pvt(active_pvt);
01142 active_pvt = ref_pvt(pvt);
01143 ast_rwlock_unlock(&active_lock);
01144 }
01145
01146 static char *cli_console_active(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01147 {
01148 struct console_pvt *pvt;
01149
01150 switch (cmd) {
01151 case CLI_INIT:
01152 e->command = "console active";
01153 e->usage =
01154 "Usage: console active [device]\n"
01155 " If no device is specified. The active console device will be shown.\n"
01156 "Otherwise, the specified device will become the console device active for\n"
01157 "the Asterisk CLI.\n";
01158 return NULL;
01159 case CLI_GENERATE:
01160 if (a->pos == e->args) {
01161 struct ao2_iterator i;
01162 int x = 0;
01163 char *res = NULL;
01164 i = ao2_iterator_init(pvts, 0);
01165 while ((pvt = ao2_iterator_next(&i))) {
01166 if (++x > a->n && !strncasecmp(pvt->name, a->word, strlen(a->word)))
01167 res = ast_strdup(pvt->name);
01168 unref_pvt(pvt);
01169 if (res) {
01170 ao2_iterator_destroy(&i);
01171 return res;
01172 }
01173 }
01174 ao2_iterator_destroy(&i);
01175 }
01176 return NULL;
01177 }
01178
01179 if (a->argc < e->args)
01180 return CLI_SHOWUSAGE;
01181
01182 if (a->argc == e->args) {
01183 pvt = get_active_pvt();
01184
01185 if (!pvt)
01186 ast_cli(a->fd, "No device is currently set as the active console device.\n");
01187 else {
01188 console_pvt_lock(pvt);
01189 ast_cli(a->fd, "The active console device is '%s'.\n", pvt->name);
01190 console_pvt_unlock(pvt);
01191 pvt = unref_pvt(pvt);
01192 }
01193
01194 return CLI_SUCCESS;
01195 }
01196
01197 if (!(pvt = find_pvt(a->argv[e->args]))) {
01198 ast_cli(a->fd, "Could not find a device called '%s'.\n", a->argv[e->args]);
01199 return CLI_FAILURE;
01200 }
01201
01202 set_active(pvt, "yes");
01203
01204 console_pvt_lock(pvt);
01205 ast_cli(a->fd, "The active console device has been set to '%s'\n", pvt->name);
01206 console_pvt_unlock(pvt);
01207
01208 unref_pvt(pvt);
01209
01210 return CLI_SUCCESS;
01211 }
01212
01213 static struct ast_cli_entry cli_console[] = {
01214 AST_CLI_DEFINE(cli_console_dial, "Dial an extension from the console"),
01215 AST_CLI_DEFINE(cli_console_hangup, "Hangup a call on the console"),
01216 AST_CLI_DEFINE(cli_console_mute, "Disable/Enable mic input"),
01217 AST_CLI_DEFINE(cli_console_answer, "Answer an incoming console call"),
01218 AST_CLI_DEFINE(cli_console_sendtext, "Send text to a connected party"),
01219 AST_CLI_DEFINE(cli_console_flash, "Send a flash to the connected party"),
01220 AST_CLI_DEFINE(cli_console_autoanswer, "Turn autoanswer on or off"),
01221 AST_CLI_DEFINE(cli_list_available, "List available devices"),
01222 AST_CLI_DEFINE(cli_list_devices, "List configured devices"),
01223 AST_CLI_DEFINE(cli_console_active, "View or Set the active console device"),
01224 };
01225
01226
01227
01228
01229
01230
01231 static void set_pvt_defaults(struct console_pvt *pvt)
01232 {
01233 if (pvt == &globals) {
01234 ast_string_field_set(pvt, mohinterpret, "default");
01235 ast_string_field_set(pvt, context, "default");
01236 ast_string_field_set(pvt, exten, "s");
01237 ast_string_field_set(pvt, language, "");
01238 ast_string_field_set(pvt, cid_num, "");
01239 ast_string_field_set(pvt, cid_name, "");
01240 ast_string_field_set(pvt, parkinglot, "");
01241
01242 pvt->overridecontext = 0;
01243 pvt->autoanswer = 0;
01244 } else {
01245 ast_mutex_lock(&globals_lock);
01246
01247 ast_string_field_set(pvt, mohinterpret, globals.mohinterpret);
01248 ast_string_field_set(pvt, context, globals.context);
01249 ast_string_field_set(pvt, exten, globals.exten);
01250 ast_string_field_set(pvt, language, globals.language);
01251 ast_string_field_set(pvt, cid_num, globals.cid_num);
01252 ast_string_field_set(pvt, cid_name, globals.cid_name);
01253 ast_string_field_set(pvt, parkinglot, globals.parkinglot);
01254
01255 pvt->overridecontext = globals.overridecontext;
01256 pvt->autoanswer = globals.autoanswer;
01257
01258 ast_mutex_unlock(&globals_lock);
01259 }
01260 }
01261
01262 static void store_callerid(struct console_pvt *pvt, const char *value)
01263 {
01264 char cid_name[256];
01265 char cid_num[256];
01266
01267 ast_callerid_split(value, cid_name, sizeof(cid_name),
01268 cid_num, sizeof(cid_num));
01269
01270 ast_string_field_set(pvt, cid_name, cid_name);
01271 ast_string_field_set(pvt, cid_num, cid_num);
01272 }
01273
01274
01275
01276
01277
01278
01279 static void store_config_core(struct console_pvt *pvt, const char *var, const char *value)
01280 {
01281 if (pvt == &globals && !ast_jb_read_conf(&global_jbconf, var, value))
01282 return;
01283
01284 CV_START(var, value);
01285
01286 CV_STRFIELD("context", pvt, context);
01287 CV_STRFIELD("extension", pvt, exten);
01288 CV_STRFIELD("mohinterpret", pvt, mohinterpret);
01289 CV_STRFIELD("language", pvt, language);
01290 CV_F("callerid", store_callerid(pvt, value));
01291 CV_BOOL("overridecontext", pvt->overridecontext);
01292 CV_BOOL("autoanswer", pvt->autoanswer);
01293 CV_STRFIELD("parkinglot", pvt, parkinglot);
01294
01295 if (pvt != &globals) {
01296 CV_F("active", set_active(pvt, value))
01297 CV_STRFIELD("input_device", pvt, input_device);
01298 CV_STRFIELD("output_device", pvt, output_device);
01299 }
01300
01301 ast_log(LOG_WARNING, "Unknown option '%s'\n", var);
01302
01303 CV_END;
01304 }
01305
01306 static void pvt_destructor(void *obj)
01307 {
01308 struct console_pvt *pvt = obj;
01309
01310 ast_string_field_free_memory(pvt);
01311 }
01312
01313 static int init_pvt(struct console_pvt *pvt, const char *name)
01314 {
01315 pvt->thread = AST_PTHREADT_NULL;
01316
01317 if (ast_string_field_init(pvt, 32))
01318 return -1;
01319
01320 ast_string_field_set(pvt, name, S_OR(name, ""));
01321
01322 return 0;
01323 }
01324
01325 static void build_device(struct ast_config *cfg, const char *name)
01326 {
01327 struct ast_variable *v;
01328 struct console_pvt *pvt;
01329 int new = 0;
01330
01331 if ((pvt = find_pvt(name))) {
01332 console_pvt_lock(pvt);
01333 set_pvt_defaults(pvt);
01334 pvt->destroy = 0;
01335 } else {
01336 if (!(pvt = ao2_alloc(sizeof(*pvt), pvt_destructor)))
01337 return;
01338 init_pvt(pvt, name);
01339 set_pvt_defaults(pvt);
01340 new = 1;
01341 }
01342
01343 for (v = ast_variable_browse(cfg, name); v; v = v->next)
01344 store_config_core(pvt, v->name, v->value);
01345
01346 if (new)
01347 ao2_link(pvts, pvt);
01348 else
01349 console_pvt_unlock(pvt);
01350
01351 unref_pvt(pvt);
01352 }
01353
01354 static int pvt_mark_destroy_cb(void *obj, void *arg, int flags)
01355 {
01356 struct console_pvt *pvt = obj;
01357 pvt->destroy = 1;
01358 return 0;
01359 }
01360
01361 static void destroy_pvts(void)
01362 {
01363 struct ao2_iterator i;
01364 struct console_pvt *pvt;
01365
01366 i = ao2_iterator_init(pvts, 0);
01367 while ((pvt = ao2_iterator_next(&i))) {
01368 if (pvt->destroy) {
01369 ao2_unlink(pvts, pvt);
01370 ast_rwlock_wrlock(&active_lock);
01371 if (active_pvt == pvt)
01372 active_pvt = unref_pvt(pvt);
01373 ast_rwlock_unlock(&active_lock);
01374 }
01375 unref_pvt(pvt);
01376 }
01377 ao2_iterator_destroy(&i);
01378 }
01379
01380
01381
01382
01383
01384
01385
01386 static int load_config(int reload)
01387 {
01388 struct ast_config *cfg;
01389 struct ast_variable *v;
01390 struct ast_flags config_flags = { 0 };
01391 char *context = NULL;
01392
01393
01394 memcpy(&global_jbconf, &default_jbconf, sizeof(global_jbconf));
01395 ast_mutex_lock(&globals_lock);
01396 set_pvt_defaults(&globals);
01397 ast_mutex_unlock(&globals_lock);
01398
01399 if (!(cfg = ast_config_load(config_file, config_flags))) {
01400 ast_log(LOG_NOTICE, "Unable to open configuration file %s!\n", config_file);
01401 return -1;
01402 }
01403
01404 ao2_callback(pvts, OBJ_NODATA, pvt_mark_destroy_cb, NULL);
01405
01406 ast_mutex_lock(&globals_lock);
01407 for (v = ast_variable_browse(cfg, "general"); v; v = v->next)
01408 store_config_core(&globals, v->name, v->value);
01409 ast_mutex_unlock(&globals_lock);
01410
01411 while ((context = ast_category_browse(cfg, context))) {
01412 if (strcasecmp(context, "general"))
01413 build_device(cfg, context);
01414 }
01415
01416 ast_config_destroy(cfg);
01417
01418 destroy_pvts();
01419
01420 return 0;
01421 }
01422
01423 static int pvt_hash_cb(const void *obj, const int flags)
01424 {
01425 const struct console_pvt *pvt = obj;
01426
01427 return ast_str_case_hash(pvt->name);
01428 }
01429
01430 static int pvt_cmp_cb(void *obj, void *arg, int flags)
01431 {
01432 struct console_pvt *pvt = obj, *pvt2 = arg;
01433
01434 return !strcasecmp(pvt->name, pvt2->name) ? CMP_MATCH | CMP_STOP : 0;
01435 }
01436
01437 static void stop_streams(void)
01438 {
01439 struct console_pvt *pvt;
01440 struct ao2_iterator i;
01441
01442 i = ao2_iterator_init(pvts, 0);
01443 while ((pvt = ao2_iterator_next(&i))) {
01444 if (pvt->hookstate)
01445 stop_stream(pvt);
01446 unref_pvt(pvt);
01447 }
01448 ao2_iterator_destroy(&i);
01449 }
01450
01451 static int unload_module(void)
01452 {
01453 ast_channel_unregister(&console_tech);
01454 ast_cli_unregister_multiple(cli_console, ARRAY_LEN(cli_console));
01455
01456 stop_streams();
01457
01458 Pa_Terminate();
01459
01460
01461 ao2_ref(pvts, -1);
01462
01463 pvt_destructor(&globals);
01464
01465 return 0;
01466 }
01467
01468 static int load_module(void)
01469 {
01470 PaError res;
01471
01472 init_pvt(&globals, NULL);
01473
01474 if (!(pvts = ao2_container_alloc(NUM_PVT_BUCKETS, pvt_hash_cb, pvt_cmp_cb)))
01475 goto return_error;
01476
01477 if (load_config(0))
01478 goto return_error;
01479
01480 res = Pa_Initialize();
01481 if (res != paNoError) {
01482 ast_log(LOG_WARNING, "Failed to initialize audio system - (%d) %s\n",
01483 res, Pa_GetErrorText(res));
01484 goto return_error_pa_init;
01485 }
01486
01487 if (ast_channel_register(&console_tech)) {
01488 ast_log(LOG_ERROR, "Unable to register channel type 'Console'\n");
01489 goto return_error_chan_reg;
01490 }
01491
01492 if (ast_cli_register_multiple(cli_console, ARRAY_LEN(cli_console)))
01493 goto return_error_cli_reg;
01494
01495 return AST_MODULE_LOAD_SUCCESS;
01496
01497 return_error_cli_reg:
01498 ast_cli_unregister_multiple(cli_console, ARRAY_LEN(cli_console));
01499 return_error_chan_reg:
01500 ast_channel_unregister(&console_tech);
01501 return_error_pa_init:
01502 Pa_Terminate();
01503 return_error:
01504 if (pvts)
01505 ao2_ref(pvts, -1);
01506 pvts = NULL;
01507 pvt_destructor(&globals);
01508
01509 return AST_MODULE_LOAD_DECLINE;
01510 }
01511
01512 static int reload(void)
01513 {
01514 return load_config(1);
01515 }
01516
01517 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Console Channel Driver",
01518 .load = load_module,
01519 .unload = unload_module,
01520 .reload = reload,
01521 );