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: 222186 $")
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 ao2_iterator_destroy(&i);
01024
01025 ast_cli(a->fd, "=============================================================\n\n");
01026
01027 return CLI_SUCCESS;
01028 }
01029
01030
01031
01032 static char *cli_console_answer(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01033 {
01034 struct ast_frame f = { AST_FRAME_CONTROL, AST_CONTROL_ANSWER };
01035 struct console_pvt *pvt = get_active_pvt();
01036
01037 switch (cmd) {
01038 case CLI_INIT:
01039 e->command = "console answer";
01040 e->usage =
01041 "Usage: console answer\n"
01042 " Answers an incoming call on the console channel.\n";
01043 return NULL;
01044
01045 case CLI_GENERATE:
01046 return NULL;
01047 }
01048
01049 if (!pvt) {
01050 ast_cli(a->fd, "No console device is set as active\n");
01051 return CLI_FAILURE;
01052 }
01053
01054 if (a->argc != e->args) {
01055 unref_pvt(pvt);
01056 return CLI_SHOWUSAGE;
01057 }
01058
01059 if (!pvt->owner) {
01060 ast_cli(a->fd, "No one is calling us\n");
01061 unref_pvt(pvt);
01062 return CLI_FAILURE;
01063 }
01064
01065 pvt->hookstate = 1;
01066
01067 ast_indicate(pvt->owner, -1);
01068
01069 ast_queue_frame(pvt->owner, &f);
01070
01071 unref_pvt(pvt);
01072
01073 return CLI_SUCCESS;
01074 }
01075
01076
01077
01078
01079
01080
01081
01082 static char *cli_console_sendtext(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01083 {
01084 char buf[TEXT_SIZE];
01085 struct console_pvt *pvt = get_active_pvt();
01086 struct ast_frame f = {
01087 .frametype = AST_FRAME_TEXT,
01088 .data.ptr = buf,
01089 .src = "console_send_text",
01090 };
01091 int len;
01092
01093 if (cmd == CLI_INIT) {
01094 e->command = "console send text";
01095 e->usage =
01096 "Usage: console send text <message>\n"
01097 " Sends a text message for display on the remote terminal.\n";
01098 return NULL;
01099 } else if (cmd == CLI_GENERATE)
01100 return NULL;
01101
01102 if (!pvt) {
01103 ast_cli(a->fd, "No console device is set as active\n");
01104 return CLI_FAILURE;
01105 }
01106
01107 if (a->argc < e->args + 1) {
01108 unref_pvt(pvt);
01109 return CLI_SHOWUSAGE;
01110 }
01111
01112 if (!pvt->owner) {
01113 ast_cli(a->fd, "Not in a call\n");
01114 unref_pvt(pvt);
01115 return CLI_FAILURE;
01116 }
01117
01118 ast_join(buf, sizeof(buf) - 1, a->argv + e->args);
01119 if (ast_strlen_zero(buf)) {
01120 unref_pvt(pvt);
01121 return CLI_SHOWUSAGE;
01122 }
01123
01124 len = strlen(buf);
01125 buf[len] = '\n';
01126 f.datalen = len + 1;
01127
01128 ast_queue_frame(pvt->owner, &f);
01129
01130 unref_pvt(pvt);
01131
01132 return CLI_SUCCESS;
01133 }
01134
01135 static void set_active(struct console_pvt *pvt, const char *value)
01136 {
01137 if (pvt == &globals) {
01138 ast_log(LOG_ERROR, "active is only valid as a per-device setting\n");
01139 return;
01140 }
01141
01142 if (!ast_true(value))
01143 return;
01144
01145 ast_rwlock_wrlock(&active_lock);
01146 if (active_pvt)
01147 unref_pvt(active_pvt);
01148 active_pvt = ref_pvt(pvt);
01149 ast_rwlock_unlock(&active_lock);
01150 }
01151
01152 static char *cli_console_active(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01153 {
01154 struct console_pvt *pvt;
01155
01156 switch (cmd) {
01157 case CLI_INIT:
01158 e->command = "console active";
01159 e->usage =
01160 "Usage: console active [device]\n"
01161 " If no device is specified. The active console device will be shown.\n"
01162 "Otherwise, the specified device will become the console device active for\n"
01163 "the Asterisk CLI.\n";
01164 return NULL;
01165 case CLI_GENERATE:
01166 if (a->pos == e->args) {
01167 struct ao2_iterator i;
01168 int x = 0;
01169 char *res = NULL;
01170 i = ao2_iterator_init(pvts, 0);
01171 while ((pvt = ao2_iterator_next(&i))) {
01172 if (++x > a->n && !strncasecmp(pvt->name, a->word, strlen(a->word)))
01173 res = ast_strdup(pvt->name);
01174 unref_pvt(pvt);
01175 if (res) {
01176 ao2_iterator_destroy(&i);
01177 return res;
01178 }
01179 }
01180 ao2_iterator_destroy(&i);
01181 }
01182 return NULL;
01183 }
01184
01185 if (a->argc < e->args)
01186 return CLI_SHOWUSAGE;
01187
01188 if (a->argc == e->args) {
01189 pvt = get_active_pvt();
01190
01191 if (!pvt)
01192 ast_cli(a->fd, "No device is currently set as the active console device.\n");
01193 else {
01194 console_pvt_lock(pvt);
01195 ast_cli(a->fd, "The active console device is '%s'.\n", pvt->name);
01196 console_pvt_unlock(pvt);
01197 pvt = unref_pvt(pvt);
01198 }
01199
01200 return CLI_SUCCESS;
01201 }
01202
01203 if (!(pvt = find_pvt(a->argv[e->args]))) {
01204 ast_cli(a->fd, "Could not find a device called '%s'.\n", a->argv[e->args]);
01205 return CLI_FAILURE;
01206 }
01207
01208 set_active(pvt, "yes");
01209
01210 console_pvt_lock(pvt);
01211 ast_cli(a->fd, "The active console device has been set to '%s'\n", pvt->name);
01212 console_pvt_unlock(pvt);
01213
01214 unref_pvt(pvt);
01215
01216 return CLI_SUCCESS;
01217 }
01218
01219 static struct ast_cli_entry cli_console[] = {
01220 AST_CLI_DEFINE(cli_console_dial, "Dial an extension from the console"),
01221 AST_CLI_DEFINE(cli_console_hangup, "Hangup a call on the console"),
01222 AST_CLI_DEFINE(cli_console_mute, "Disable/Enable mic input"),
01223 AST_CLI_DEFINE(cli_console_answer, "Answer an incoming console call"),
01224 AST_CLI_DEFINE(cli_console_sendtext, "Send text to a connected party"),
01225 AST_CLI_DEFINE(cli_console_flash, "Send a flash to the connected party"),
01226 AST_CLI_DEFINE(cli_console_autoanswer, "Turn autoanswer on or off"),
01227 AST_CLI_DEFINE(cli_list_available, "List available devices"),
01228 AST_CLI_DEFINE(cli_list_devices, "List configured devices"),
01229 AST_CLI_DEFINE(cli_console_active, "View or Set the active console device"),
01230 };
01231
01232
01233
01234
01235
01236
01237 static void set_pvt_defaults(struct console_pvt *pvt)
01238 {
01239 if (pvt == &globals) {
01240 ast_string_field_set(pvt, mohinterpret, "default");
01241 ast_string_field_set(pvt, context, "default");
01242 ast_string_field_set(pvt, exten, "s");
01243 ast_string_field_set(pvt, language, "");
01244 ast_string_field_set(pvt, cid_num, "");
01245 ast_string_field_set(pvt, cid_name, "");
01246 ast_string_field_set(pvt, parkinglot, "");
01247
01248 pvt->overridecontext = 0;
01249 pvt->autoanswer = 0;
01250 } else {
01251 ast_mutex_lock(&globals_lock);
01252
01253 ast_string_field_set(pvt, mohinterpret, globals.mohinterpret);
01254 ast_string_field_set(pvt, context, globals.context);
01255 ast_string_field_set(pvt, exten, globals.exten);
01256 ast_string_field_set(pvt, language, globals.language);
01257 ast_string_field_set(pvt, cid_num, globals.cid_num);
01258 ast_string_field_set(pvt, cid_name, globals.cid_name);
01259 ast_string_field_set(pvt, parkinglot, globals.parkinglot);
01260
01261 pvt->overridecontext = globals.overridecontext;
01262 pvt->autoanswer = globals.autoanswer;
01263
01264 ast_mutex_unlock(&globals_lock);
01265 }
01266 }
01267
01268 static void store_callerid(struct console_pvt *pvt, const char *value)
01269 {
01270 char cid_name[256];
01271 char cid_num[256];
01272
01273 ast_callerid_split(value, cid_name, sizeof(cid_name),
01274 cid_num, sizeof(cid_num));
01275
01276 ast_string_field_set(pvt, cid_name, cid_name);
01277 ast_string_field_set(pvt, cid_num, cid_num);
01278 }
01279
01280
01281
01282
01283
01284
01285 static void store_config_core(struct console_pvt *pvt, const char *var, const char *value)
01286 {
01287 if (pvt == &globals && !ast_jb_read_conf(&global_jbconf, var, value))
01288 return;
01289
01290 CV_START(var, value);
01291
01292 CV_STRFIELD("context", pvt, context);
01293 CV_STRFIELD("extension", pvt, exten);
01294 CV_STRFIELD("mohinterpret", pvt, mohinterpret);
01295 CV_STRFIELD("language", pvt, language);
01296 CV_F("callerid", store_callerid(pvt, value));
01297 CV_BOOL("overridecontext", pvt->overridecontext);
01298 CV_BOOL("autoanswer", pvt->autoanswer);
01299 CV_STRFIELD("parkinglot", pvt, parkinglot);
01300
01301 if (pvt != &globals) {
01302 CV_F("active", set_active(pvt, value))
01303 CV_STRFIELD("input_device", pvt, input_device);
01304 CV_STRFIELD("output_device", pvt, output_device);
01305 }
01306
01307 ast_log(LOG_WARNING, "Unknown option '%s'\n", var);
01308
01309 CV_END;
01310 }
01311
01312 static void pvt_destructor(void *obj)
01313 {
01314 struct console_pvt *pvt = obj;
01315
01316 ast_string_field_free_memory(pvt);
01317 }
01318
01319 static int init_pvt(struct console_pvt *pvt, const char *name)
01320 {
01321 pvt->thread = AST_PTHREADT_NULL;
01322
01323 if (ast_string_field_init(pvt, 32))
01324 return -1;
01325
01326 ast_string_field_set(pvt, name, S_OR(name, ""));
01327
01328 return 0;
01329 }
01330
01331 static void build_device(struct ast_config *cfg, const char *name)
01332 {
01333 struct ast_variable *v;
01334 struct console_pvt *pvt;
01335 int new = 0;
01336
01337 if ((pvt = find_pvt(name))) {
01338 console_pvt_lock(pvt);
01339 set_pvt_defaults(pvt);
01340 pvt->destroy = 0;
01341 } else {
01342 if (!(pvt = ao2_alloc(sizeof(*pvt), pvt_destructor)))
01343 return;
01344 init_pvt(pvt, name);
01345 set_pvt_defaults(pvt);
01346 new = 1;
01347 }
01348
01349 for (v = ast_variable_browse(cfg, name); v; v = v->next)
01350 store_config_core(pvt, v->name, v->value);
01351
01352 if (new)
01353 ao2_link(pvts, pvt);
01354 else
01355 console_pvt_unlock(pvt);
01356
01357 unref_pvt(pvt);
01358 }
01359
01360 static int pvt_mark_destroy_cb(void *obj, void *arg, int flags)
01361 {
01362 struct console_pvt *pvt = obj;
01363 pvt->destroy = 1;
01364 return 0;
01365 }
01366
01367 static void destroy_pvts(void)
01368 {
01369 struct ao2_iterator i;
01370 struct console_pvt *pvt;
01371
01372 i = ao2_iterator_init(pvts, 0);
01373 while ((pvt = ao2_iterator_next(&i))) {
01374 if (pvt->destroy) {
01375 ao2_unlink(pvts, pvt);
01376 ast_rwlock_wrlock(&active_lock);
01377 if (active_pvt == pvt)
01378 active_pvt = unref_pvt(pvt);
01379 ast_rwlock_unlock(&active_lock);
01380 }
01381 unref_pvt(pvt);
01382 }
01383 ao2_iterator_destroy(&i);
01384 }
01385
01386
01387
01388
01389
01390
01391
01392 static int load_config(int reload)
01393 {
01394 struct ast_config *cfg;
01395 struct ast_variable *v;
01396 struct ast_flags config_flags = { 0 };
01397 char *context = NULL;
01398
01399
01400 memcpy(&global_jbconf, &default_jbconf, sizeof(global_jbconf));
01401 ast_mutex_lock(&globals_lock);
01402 set_pvt_defaults(&globals);
01403 ast_mutex_unlock(&globals_lock);
01404
01405 if (!(cfg = ast_config_load(config_file, config_flags))) {
01406 ast_log(LOG_NOTICE, "Unable to open configuration file %s!\n", config_file);
01407 return -1;
01408 }
01409
01410 ao2_callback(pvts, OBJ_NODATA, pvt_mark_destroy_cb, NULL);
01411
01412 ast_mutex_lock(&globals_lock);
01413 for (v = ast_variable_browse(cfg, "general"); v; v = v->next)
01414 store_config_core(&globals, v->name, v->value);
01415 ast_mutex_unlock(&globals_lock);
01416
01417 while ((context = ast_category_browse(cfg, context))) {
01418 if (strcasecmp(context, "general"))
01419 build_device(cfg, context);
01420 }
01421
01422 ast_config_destroy(cfg);
01423
01424 destroy_pvts();
01425
01426 return 0;
01427 }
01428
01429 static int pvt_hash_cb(const void *obj, const int flags)
01430 {
01431 const struct console_pvt *pvt = obj;
01432
01433 return ast_str_case_hash(pvt->name);
01434 }
01435
01436 static int pvt_cmp_cb(void *obj, void *arg, int flags)
01437 {
01438 struct console_pvt *pvt = obj, *pvt2 = arg;
01439
01440 return !strcasecmp(pvt->name, pvt2->name) ? CMP_MATCH | CMP_STOP : 0;
01441 }
01442
01443 static void stop_streams(void)
01444 {
01445 struct console_pvt *pvt;
01446 struct ao2_iterator i;
01447
01448 i = ao2_iterator_init(pvts, 0);
01449 while ((pvt = ao2_iterator_next(&i))) {
01450 if (pvt->hookstate)
01451 stop_stream(pvt);
01452 unref_pvt(pvt);
01453 }
01454 ao2_iterator_destroy(&i);
01455 }
01456
01457 static int unload_module(void)
01458 {
01459 ast_channel_unregister(&console_tech);
01460 ast_cli_unregister_multiple(cli_console, ARRAY_LEN(cli_console));
01461
01462 stop_streams();
01463
01464 Pa_Terminate();
01465
01466
01467 ao2_ref(pvts, -1);
01468
01469 pvt_destructor(&globals);
01470
01471 return 0;
01472 }
01473
01474 static int load_module(void)
01475 {
01476 PaError res;
01477
01478 init_pvt(&globals, NULL);
01479
01480 if (!(pvts = ao2_container_alloc(NUM_PVT_BUCKETS, pvt_hash_cb, pvt_cmp_cb)))
01481 goto return_error;
01482
01483 if (load_config(0))
01484 goto return_error;
01485
01486 res = Pa_Initialize();
01487 if (res != paNoError) {
01488 ast_log(LOG_WARNING, "Failed to initialize audio system - (%d) %s\n",
01489 res, Pa_GetErrorText(res));
01490 goto return_error_pa_init;
01491 }
01492
01493 if (ast_channel_register(&console_tech)) {
01494 ast_log(LOG_ERROR, "Unable to register channel type 'Console'\n");
01495 goto return_error_chan_reg;
01496 }
01497
01498 if (ast_cli_register_multiple(cli_console, ARRAY_LEN(cli_console)))
01499 goto return_error_cli_reg;
01500
01501 return AST_MODULE_LOAD_SUCCESS;
01502
01503 return_error_cli_reg:
01504 ast_cli_unregister_multiple(cli_console, ARRAY_LEN(cli_console));
01505 return_error_chan_reg:
01506 ast_channel_unregister(&console_tech);
01507 return_error_pa_init:
01508 Pa_Terminate();
01509 return_error:
01510 if (pvts)
01511 ao2_ref(pvts, -1);
01512 pvt_destructor(&globals);
01513
01514 return AST_MODULE_LOAD_DECLINE;
01515 }
01516
01517 static int reload(void)
01518 {
01519 return load_config(1);
01520 }
01521
01522 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Console Channel Driver",
01523 .load = load_module,
01524 .unload = unload_module,
01525 .reload = reload,
01526 );