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