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