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