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