diff options
Diffstat (limited to 'channels/chan_console.c')
-rw-r--r-- | channels/chan_console.c | 545 |
1 files changed, 446 insertions, 99 deletions
diff --git a/channels/chan_console.c b/channels/chan_console.c index 58f456c39..59bdcac26 100644 --- a/channels/chan_console.c +++ b/channels/chan_console.c @@ -1,7 +1,7 @@ /* * Asterisk -- An open source telephony toolkit. * - * Copyright (C) 2006 - 2007, Digium, Inc. + * Copyright (C) 2006 - 2008, Digium, Inc. * * Russell Bryant <russell@digium.com> * @@ -68,6 +68,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/cli.h" #include "asterisk/musiconhold.h" #include "asterisk/callerid.h" +#include "asterisk/astobj2.h" /*! * \brief The sample rate to request from PortAudio @@ -126,6 +127,8 @@ static struct console_pvt { AST_DECLARE_STRING_FIELDS( /*! Name of the device */ AST_STRING_FIELD(name); + AST_STRING_FIELD(input_device); + AST_STRING_FIELD(output_device); /*! Default context for outgoing calls */ AST_STRING_FIELD(context); /*! Default extension for outgoing calls */ @@ -157,14 +160,20 @@ static struct console_pvt { unsigned int autoanswer:1; /*! Ignore context in the console dial CLI command */ unsigned int overridecontext:1; - /*! Lock to protect data in this struct */ - ast_mutex_t __lock; + /*! Set during a reload so that we know to destroy this if it is no longer + * in the configuration file. */ + unsigned int destroy:1; /*! ID for the stream monitor thread */ pthread_t thread; -} console_pvt = { - .__lock = AST_MUTEX_INIT_VALUE, - .thread = AST_PTHREADT_NULL, -}; +} globals; + +AST_MUTEX_DEFINE_STATIC(globals_lock); + +static struct ao2_container *pvts; +#define NUM_PVT_BUCKETS 7 + +static struct console_pvt *active_pvt; +AST_RWLOCK_DEFINE_STATIC(active_lock); /*! * \brief Global jitterbuffer configuration @@ -218,10 +227,32 @@ static const struct ast_channel_tech console_tech = { }; /*! \brief lock a console_pvt struct */ -#define console_pvt_lock(pvt) ast_mutex_lock(&(pvt)->__lock) +#define console_pvt_lock(pvt) ao2_lock(pvt) /*! \brief unlock a console_pvt struct */ -#define console_pvt_unlock(pvt) ast_mutex_unlock(&(pvt)->__lock) +#define console_pvt_unlock(pvt) ao2_unlock(pvt) + +static inline struct console_pvt *ref_pvt(struct console_pvt *pvt) +{ + if (pvt) + ao2_ref(pvt, +1); + return pvt; +} + +static inline struct console_pvt *unref_pvt(struct console_pvt *pvt) +{ + ao2_ref(pvt, -1); + return NULL; +} + +static struct console_pvt *find_pvt(const char *name) +{ + struct console_pvt tmp_pvt = { + .name = name, + }; + + return ao2_find(pvts, &tmp_pvt, OBJ_POINTER); +} /*! * \brief Stream monitor thread @@ -259,6 +290,67 @@ static void *stream_monitor(void *data) return NULL; } +static int open_stream(struct console_pvt *pvt) +{ + int res = paInternalError; + + if (!strcasecmp(pvt->input_device, "default") && + !strcasecmp(pvt->output_device, "default")) { + res = Pa_OpenDefaultStream(&pvt->stream, INPUT_CHANNELS, OUTPUT_CHANNELS, + paInt16, SAMPLE_RATE, NUM_SAMPLES, NULL, NULL); + } else { + PaStreamParameters input_params = { + .channelCount = 1, + .sampleFormat = paInt16, + .suggestedLatency = (1.0 / 50.0), /* 20 ms */ + .device = paNoDevice, + }; + PaStreamParameters output_params = { + .channelCount = 1, + .sampleFormat = paInt16, + .suggestedLatency = (1.0 / 50.0), /* 20 ms */ + .device = paNoDevice, + }; + PaDeviceIndex index, num_devices, def_input, def_output; + + if (!(num_devices = Pa_GetDeviceCount())) + return res; + + def_input = Pa_GetDefaultInputDevice(); + def_output = Pa_GetDefaultOutputDevice(); + + for (index = 0; + index < num_devices && (input_params.device == paNoDevice + || output_params.device == paNoDevice); + index++) + { + const PaDeviceInfo *dev = Pa_GetDeviceInfo(index); + + if (dev->maxInputChannels) { + if ( (index == def_input && !strcasecmp(pvt->input_device, "default")) || + !strcasecmp(pvt->input_device, dev->name) ) + input_params.device = index; + } + + if (dev->maxOutputChannels) { + if ( (index == def_output && !strcasecmp(pvt->output_device, "default")) || + !strcasecmp(pvt->output_device, dev->name) ) + output_params.device = index; + } + } + + if (input_params.device == paNoDevice) + ast_log(LOG_ERROR, "No input device found for console device '%s'\n", pvt->name); + if (output_params.device == paNoDevice) + ast_log(LOG_ERROR, "No output device found for console device '%s'\n", pvt->name); + + res = Pa_OpenStream(&pvt->stream, &input_params, &output_params, + SAMPLE_RATE, NUM_SAMPLES, paNoFlag, NULL, NULL); + } + + return res; +} + static int start_stream(struct console_pvt *pvt) { PaError res; @@ -272,10 +364,9 @@ static int start_stream(struct console_pvt *pvt) pvt->streamstate = 1; ast_debug(1, "Starting stream\n"); - res = Pa_OpenDefaultStream(&pvt->stream, INPUT_CHANNELS, OUTPUT_CHANNELS, - paInt16, SAMPLE_RATE, NUM_SAMPLES, NULL, NULL); + res = open_stream(pvt); if (res != paNoError) { - ast_log(LOG_WARNING, "Failed to open default audio device - (%d) %s\n", + ast_log(LOG_WARNING, "Failed to open stream - (%d) %s\n", res, Pa_GetErrorText(res)); ret_val = -1; goto return_unlock; @@ -335,7 +426,7 @@ static struct ast_channel *console_new(struct console_pvt *pvt, const char *ext, chan->nativeformats = AST_FORMAT_SLINEAR16; chan->readformat = AST_FORMAT_SLINEAR16; chan->writeformat = AST_FORMAT_SLINEAR16; - chan->tech_pvt = pvt; + chan->tech_pvt = ref_pvt(pvt); pvt->owner = chan; @@ -359,19 +450,24 @@ static struct ast_channel *console_new(struct console_pvt *pvt, const char *ext, static struct ast_channel *console_request(const char *type, int format, void *data, int *cause) { int oldformat = format; - struct ast_channel *chan; - struct console_pvt *pvt = &console_pvt; + struct ast_channel *chan = NULL; + struct console_pvt *pvt; + + if (!(pvt = find_pvt(data))) { + ast_log(LOG_ERROR, "Console device '%s' not found\n", (char *) data); + return NULL; + } format &= SUPPORTED_FORMATS; if (!format) { ast_log(LOG_NOTICE, "Channel requested with unsupported format(s): '%d'\n", oldformat); - return NULL; + goto return_unref; } if (pvt->owner) { ast_log(LOG_NOTICE, "Console channel already active!\n"); *cause = AST_CAUSE_BUSY; - return NULL; + goto return_unref; } console_pvt_lock(pvt); @@ -381,6 +477,9 @@ static struct ast_channel *console_request(const char *type, int format, void *d if (!chan) ast_log(LOG_WARNING, "Unable to create new Console channel!\n"); +return_unref: + unref_pvt(pvt); + return chan; } @@ -408,22 +507,22 @@ static int console_text(struct ast_channel *c, const char *text) static int console_hangup(struct ast_channel *c) { - struct console_pvt *pvt = &console_pvt; + struct console_pvt *pvt = c->tech_pvt; ast_verb(1, V_BEGIN "Hangup on Console" V_END); pvt->hookstate = 0; - c->tech_pvt = NULL; pvt->owner = NULL; - stop_stream(pvt); + c->tech_pvt = unref_pvt(pvt); + return 0; } static int console_answer(struct ast_channel *c) { - struct console_pvt *pvt = &console_pvt; + struct console_pvt *pvt = c->tech_pvt; ast_verb(1, V_BEGIN "Call from Console has been Answered" V_END); @@ -462,7 +561,7 @@ static struct ast_frame *console_read(struct ast_channel *chan) static int console_call(struct ast_channel *c, char *dest, int timeout) { struct ast_frame f = { 0, }; - struct console_pvt *pvt = &console_pvt; + struct console_pvt *pvt = c->tech_pvt; ast_verb(1, V_BEGIN "Call to device '%s' on console from '%s' <%s>" V_END, dest, c->cid.cid_name, c->cid.cid_num); @@ -491,7 +590,7 @@ static int console_call(struct ast_channel *c, char *dest, int timeout) static int console_write(struct ast_channel *chan, struct ast_frame *f) { - struct console_pvt *pvt = &console_pvt; + struct console_pvt *pvt = chan->tech_pvt; Pa_WriteStream(pvt->stream, f->data, f->samples); @@ -534,7 +633,7 @@ static int console_indicate(struct ast_channel *chan, int cond, const void *data static int console_fixup(struct ast_channel *oldchan, struct ast_channel *newchan) { - struct console_pvt *pvt = &console_pvt; + struct console_pvt *pvt = newchan->tech_pvt; pvt->owner = newchan; @@ -575,10 +674,22 @@ static char *ast_ext_ctx(struct console_pvt *pvt, const char *src, char **ext, c return *ext; } +static struct console_pvt *get_active_pvt(void) +{ + struct console_pvt *pvt; + + ast_rwlock_rdlock(&active_lock); + pvt = ref_pvt(active_pvt); + ast_rwlock_unlock(&active_lock); + + return pvt; +} + static char *cli_console_autoanswer(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { - struct console_pvt *pvt = &console_pvt; + struct console_pvt *pvt = get_active_pvt(); + char *res = CLI_SUCCESS; switch (cmd) { case CLI_INIT: @@ -594,18 +705,20 @@ static char *cli_console_autoanswer(struct ast_cli_entry *e, int cmd, return NULL; } + if (!pvt) { + ast_cli(a->fd, "No console device is set as active.\n"); + return CLI_FAILURE; + } + if (a->argc == e->args - 1) { ast_cli(a->fd, "Auto answer is %s.\n", pvt->autoanswer ? "on" : "off"); + unref_pvt(pvt); return CLI_SUCCESS; } - if (a->argc != e->args) + if (a->argc != e->args) { + unref_pvt(pvt); return CLI_SHOWUSAGE; - - if (!pvt) { - ast_log(LOG_WARNING, "Cannot find device %s (should not happen!)\n", - pvt->name); - return CLI_FAILURE; } if (!strcasecmp(a->argv[e->args-1], "on")) @@ -613,7 +726,9 @@ static char *cli_console_autoanswer(struct ast_cli_entry *e, int cmd, else if (!strcasecmp(a->argv[e->args - 1], "off")) pvt->autoanswer = 0; else - return CLI_SHOWUSAGE; + res = CLI_SHOWUSAGE; + + unref_pvt(pvt); return CLI_SUCCESS; } @@ -621,7 +736,7 @@ static char *cli_console_autoanswer(struct ast_cli_entry *e, int cmd, static char *cli_console_flash(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { struct ast_frame f = { AST_FRAME_CONTROL, AST_CONTROL_FLASH }; - struct console_pvt *pvt = &console_pvt; + struct console_pvt *pvt = get_active_pvt(); if (cmd == CLI_INIT) { e->command = "console flash"; @@ -632,11 +747,17 @@ static char *cli_console_flash(struct ast_cli_entry *e, int cmd, struct ast_cli_ } else if (cmd == CLI_GENERATE) return NULL; + if (!pvt) { + ast_cli(a->fd, "No console device is set as active\n"); + return CLI_FAILURE; + } + if (a->argc != e->args) return CLI_SHOWUSAGE; if (!pvt->owner) { ast_cli(a->fd, "No call to flash\n"); + unref_pvt(pvt); return CLI_FAILURE; } @@ -644,6 +765,8 @@ static char *cli_console_flash(struct ast_cli_entry *e, int cmd, struct ast_cli_ ast_queue_frame(pvt->owner, &f); + unref_pvt(pvt); + return CLI_SUCCESS; } @@ -651,7 +774,7 @@ static char *cli_console_dial(struct ast_cli_entry *e, int cmd, struct ast_cli_a { char *s = NULL; const char *mye = NULL, *myc = NULL; - struct console_pvt *pvt = &console_pvt; + struct console_pvt *pvt = get_active_pvt(); if (cmd == CLI_INIT) { e->command = "console dial"; @@ -662,6 +785,11 @@ static char *cli_console_dial(struct ast_cli_entry *e, int cmd, struct ast_cli_a } else if (cmd == CLI_GENERATE) return NULL; + if (!pvt) { + ast_cli(a->fd, "No console device is currently set as active\n"); + return CLI_FAILURE; + } + if (a->argc > e->args + 1) return CLI_SHOWUSAGE; @@ -671,6 +799,7 @@ static char *cli_console_dial(struct ast_cli_entry *e, int cmd, struct ast_cli_a if (a->argc == e->args) { /* argument is mandatory here */ ast_cli(a->fd, "Already in a call. You can only dial digits until you hangup.\n"); + unref_pvt(pvt); return CLI_FAILURE; } s = a->argv[e->args]; @@ -679,6 +808,7 @@ static char *cli_console_dial(struct ast_cli_entry *e, int cmd, struct ast_cli_a f.subclass = s[i]; ast_queue_frame(pvt->owner, &f); } + unref_pvt(pvt); return CLI_SUCCESS; } @@ -709,12 +839,14 @@ static char *cli_console_dial(struct ast_cli_entry *e, int cmd, struct ast_cli_a if (s) free(s); + unref_pvt(pvt); + return CLI_SUCCESS; } static char *cli_console_hangup(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { - struct console_pvt *pvt = &console_pvt; + struct console_pvt *pvt = get_active_pvt(); if (cmd == CLI_INIT) { e->command = "console hangup"; @@ -725,11 +857,17 @@ static char *cli_console_hangup(struct ast_cli_entry *e, int cmd, struct ast_cli } else if (cmd == CLI_GENERATE) return NULL; + if (!pvt) { + ast_cli(a->fd, "No console device is set as active\n"); + return CLI_FAILURE; + } + if (a->argc != e->args) return CLI_SHOWUSAGE; if (!pvt->owner && !pvt->hookstate) { ast_cli(a->fd, "No call to hang up\n"); + unref_pvt(pvt); return CLI_FAILURE; } @@ -737,14 +875,17 @@ static char *cli_console_hangup(struct ast_cli_entry *e, int cmd, struct ast_cli if (pvt->owner) ast_queue_hangup(pvt->owner); + unref_pvt(pvt); + return CLI_SUCCESS; } static char *cli_console_mute(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { char *s; - struct console_pvt *pvt = &console_pvt; - + struct console_pvt *pvt = get_active_pvt(); + char *res = CLI_SUCCESS; + if (cmd == CLI_INIT) { e->command = "console {mute|unmute}"; e->usage = @@ -754,6 +895,11 @@ static char *cli_console_mute(struct ast_cli_entry *e, int cmd, struct ast_cli_a } else if (cmd == CLI_GENERATE) return NULL; + if (!pvt) { + ast_cli(a->fd, "No console device is set as active\n"); + return CLI_FAILURE; + } + if (a->argc != e->args) return CLI_SHOWUSAGE; @@ -763,22 +909,24 @@ static char *cli_console_mute(struct ast_cli_entry *e, int cmd, struct ast_cli_a else if (!strcasecmp(s, "unmute")) pvt->muted = 0; else - return CLI_SHOWUSAGE; + res = CLI_SHOWUSAGE; ast_verb(1, V_BEGIN "The Console is now %s" V_END, pvt->muted ? "Muted" : "Unmuted"); - return CLI_SUCCESS; + unref_pvt(pvt); + + return res; } -static char *cli_list_devices(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +static char *cli_list_available(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { PaDeviceIndex index, num, def_input, def_output; if (cmd == CLI_INIT) { - e->command = "console list devices"; + e->command = "console list available"; e->usage = - "Usage: console list devices\n" + "Usage: console list available\n" " List all available devices.\n"; return NULL; } else if (cmd == CLI_GENERATE) @@ -819,13 +967,69 @@ static char *cli_list_devices(struct ast_cli_entry *e, int cmd, struct ast_cli_a return CLI_SUCCESS; } +static char *cli_list_devices(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + struct ao2_iterator i; + struct console_pvt *pvt; + + if (cmd == CLI_INIT) { + e->command = "console list devices"; + e->usage = + "Usage: console list devices\n" + " List all configured devices.\n"; + return NULL; + } else if (cmd == CLI_GENERATE) + return NULL; + + if (a->argc != e->args) + return CLI_SHOWUSAGE; + + ast_cli(a->fd, "\n" + "=============================================================\n" + "=== Configured Devices ======================================\n" + "=============================================================\n" + "===\n"); + + i = ao2_iterator_init(pvts, 0); + while ((pvt = ao2_iterator_next(&i))) { + console_pvt_lock(pvt); + + ast_cli(a->fd, "=== ---------------------------------------------------------\n" + "=== Device Name: %s\n" + "=== ---> Active: %s\n" + "=== ---> Input Device: %s\n" + "=== ---> Output Device: %s\n" + "=== ---> Context: %s\n" + "=== ---> Extension: %s\n" + "=== ---> CallerID Num: %s\n" + "=== ---> CallerID Name: %s\n" + "=== ---> MOH Interpret: %s\n" + "=== ---> Language: %s\n" + "=== ---> Muted: %s\n" + "=== ---> Auto-Answer: %s\n" + "=== ---> Override Context: %s\n" + "=== ---------------------------------------------------------\n===\n", + pvt->name, (pvt == active_pvt) ? "Yes" : "No", + pvt->input_device, pvt->output_device, pvt->context, + pvt->exten, pvt->cid_num, pvt->cid_name, pvt->mohinterpret, + pvt->language, pvt->muted ? "Yes" : "No", pvt->autoanswer ? "Yes" : "No", + pvt->overridecontext ? "Yes" : "No"); + + console_pvt_unlock(pvt); + unref_pvt(pvt); + } + + ast_cli(a->fd, "=============================================================\n\n"); + + return CLI_SUCCESS; +} /*! * \brief answer command from the console */ static char *cli_console_answer(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { struct ast_frame f = { AST_FRAME_CONTROL, AST_CONTROL_ANSWER }; - struct console_pvt *pvt = &console_pvt; + struct console_pvt *pvt = get_active_pvt(); switch (cmd) { case CLI_INIT: @@ -839,11 +1043,19 @@ static char *cli_console_answer(struct ast_cli_entry *e, int cmd, struct ast_cli return NULL; /* no completion */ } - if (a->argc != e->args) + if (!pvt) { + ast_cli(a->fd, "No console device is set as active\n"); + return CLI_FAILURE; + } + + if (a->argc != e->args) { + unref_pvt(pvt); return CLI_SHOWUSAGE; + } if (!pvt->owner) { ast_cli(a->fd, "No one is calling us\n"); + unref_pvt(pvt); return CLI_FAILURE; } @@ -853,6 +1065,8 @@ static char *cli_console_answer(struct ast_cli_entry *e, int cmd, struct ast_cli ast_queue_frame(pvt->owner, &f); + unref_pvt(pvt); + return CLI_SUCCESS; } @@ -865,7 +1079,7 @@ static char *cli_console_answer(struct ast_cli_entry *e, int cmd, struct ast_cli static char *cli_console_sendtext(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { char buf[TEXT_SIZE]; - struct console_pvt *pvt = &console_pvt; + struct console_pvt *pvt = get_active_pvt(); struct ast_frame f = { .frametype = AST_FRAME_TEXT, .data = buf, @@ -882,17 +1096,27 @@ static char *cli_console_sendtext(struct ast_cli_entry *e, int cmd, struct ast_c } else if (cmd == CLI_GENERATE) return NULL; - if (a->argc < e->args + 1) + if (!pvt) { + ast_cli(a->fd, "No console device is set as active\n"); + return CLI_FAILURE; + } + + if (a->argc < e->args + 1) { + unref_pvt(pvt); return CLI_SHOWUSAGE; + } if (!pvt->owner) { ast_cli(a->fd, "Not in a call\n"); + unref_pvt(pvt); return CLI_FAILURE; } ast_join(buf, sizeof(buf) - 1, a->argv + e->args); - if (ast_strlen_zero(buf)) + if (ast_strlen_zero(buf)) { + unref_pvt(pvt); return CLI_SHOWUSAGE; + } len = strlen(buf); buf[len] = '\n'; @@ -900,6 +1124,8 @@ static char *cli_console_sendtext(struct ast_cli_entry *e, int cmd, struct ast_c ast_queue_frame(pvt->owner, &f); + unref_pvt(pvt); + return CLI_SUCCESS; } @@ -911,7 +1137,8 @@ static struct ast_cli_entry cli_console[] = { AST_CLI_DEFINE(cli_console_sendtext, "Send text to a connected party"), AST_CLI_DEFINE(cli_console_flash, "Send a flash to the connected party"), AST_CLI_DEFINE(cli_console_autoanswer, "Turn autoanswer on or off"), - AST_CLI_DEFINE(cli_list_devices, "List available devices"), + AST_CLI_DEFINE(cli_list_available, "List available devices"), + AST_CLI_DEFINE(cli_list_devices, "List configured devices"), }; /*! @@ -919,24 +1146,33 @@ static struct ast_cli_entry cli_console[] = { * * \note This function expects the pvt lock to be held. */ -static void set_pvt_defaults(struct console_pvt *pvt, int reload) +static void set_pvt_defaults(struct console_pvt *pvt) { - if (!reload) { - /* This should be changed for multiple device support. Right now, - * there is no way to change the name of a device. The default - * input and output sound devices are the only ones supported. */ - ast_string_field_set(pvt, name, "default"); - } + if (pvt == &globals) { + ast_string_field_set(pvt, mohinterpret, "default"); + ast_string_field_set(pvt, context, "default"); + ast_string_field_set(pvt, exten, "s"); + ast_string_field_set(pvt, language, ""); + ast_string_field_set(pvt, cid_num, ""); + ast_string_field_set(pvt, cid_name, ""); + + pvt->overridecontext = 0; + pvt->autoanswer = 0; + } else { + ast_mutex_lock(&globals_lock); + + ast_string_field_set(pvt, mohinterpret, globals.mohinterpret); + ast_string_field_set(pvt, context, globals.context); + ast_string_field_set(pvt, exten, globals.exten); + ast_string_field_set(pvt, language, globals.language); + ast_string_field_set(pvt, cid_num, globals.cid_num); + ast_string_field_set(pvt, cid_name, globals.cid_name); - ast_string_field_set(pvt, mohinterpret, "default"); - ast_string_field_set(pvt, context, "default"); - ast_string_field_set(pvt, exten, "s"); - ast_string_field_set(pvt, language, ""); - ast_string_field_set(pvt, cid_num, ""); - ast_string_field_set(pvt, cid_name, ""); + pvt->overridecontext = globals.overridecontext; + pvt->autoanswer = globals.autoanswer; - pvt->overridecontext = 0; - pvt->autoanswer = 0; + ast_mutex_unlock(&globals_lock); + } } static void store_callerid(struct console_pvt *pvt, const char *value) @@ -951,6 +1187,23 @@ static void store_callerid(struct console_pvt *pvt, const char *value) ast_string_field_set(pvt, cid_num, cid_num); } +static void set_active(struct console_pvt *pvt, const char *value) +{ + if (pvt == &globals) { + ast_log(LOG_ERROR, "active is only valid as a per-device setting\n"); + return; + } + + if (!ast_true(value)) + return; + + ast_rwlock_wrlock(&active_lock); + if (active_pvt) + unref_pvt(active_pvt); + active_pvt = ref_pvt(pvt); + ast_rwlock_unlock(&active_lock); +} + /*! * \brief Store a configuration parameter in a pvt struct * @@ -958,7 +1211,7 @@ static void store_callerid(struct console_pvt *pvt, const char *value) */ static void store_config_core(struct console_pvt *pvt, const char *var, const char *value) { - if (!ast_jb_read_conf(&global_jbconf, var, value)) + if (pvt == &globals && !ast_jb_read_conf(&global_jbconf, var, value)) return; CV_START(var, value); @@ -970,12 +1223,91 @@ static void store_config_core(struct console_pvt *pvt, const char *var, const ch CV_F("callerid", store_callerid(pvt, value)); CV_BOOL("overridecontext", pvt->overridecontext); CV_BOOL("autoanswer", pvt->autoanswer); - + + if (pvt != &globals) { + CV_F("active", set_active(pvt, value)) + CV_STRFIELD("input_device", pvt, input_device); + CV_STRFIELD("output_device", pvt, output_device); + } + ast_log(LOG_WARNING, "Unknown option '%s'\n", var); CV_END; } +static void pvt_destructor(void *obj) +{ + struct console_pvt *pvt = obj; + + ast_string_field_free_memory(pvt); +} + +static int init_pvt(struct console_pvt *pvt, const char *name) +{ + pvt->thread = AST_PTHREADT_NULL; + + if (ast_string_field_init(pvt, 32)) + return -1; + + ast_string_field_set(pvt, name, S_OR(name, "")); + + return 0; +} + +static void build_device(struct ast_config *cfg, const char *name) +{ + struct ast_variable *v; + struct console_pvt *pvt; + int new; + + if ((pvt = find_pvt(name))) { + console_pvt_lock(pvt); + set_pvt_defaults(pvt); + pvt->destroy = 0; + } else { + if (!(pvt = ao2_alloc(sizeof(*pvt), pvt_destructor))) + return; + init_pvt(pvt, name); + set_pvt_defaults(pvt); + new = 1; + } + + for (v = ast_variable_browse(cfg, name); v; v = v->next) + store_config_core(pvt, v->name, v->value); + + if (new) + ao2_link(pvts, pvt); + else + console_pvt_unlock(pvt); + + unref_pvt(pvt); +} + +static int pvt_mark_destroy_cb(void *obj, void *arg, int flags) +{ + struct console_pvt *pvt = obj; + pvt->destroy = 1; + return 0; +} + +static void destroy_pvts(void) +{ + struct ao2_iterator i; + struct console_pvt *pvt; + + i = ao2_iterator_init(pvts, 0); + while ((pvt = ao2_iterator_next(&i))) { + if (pvt->destroy) { + ao2_unlink(pvts, pvt); + ast_rwlock_wrlock(&active_lock); + if (active_pvt == pvt) + active_pvt = unref_pvt(pvt); + ast_rwlock_unlock(&active_lock); + } + unref_pvt(pvt); + } +} + /*! * \brief Load the configuration * \param reload if this was called due to a reload @@ -986,67 +1318,79 @@ static int load_config(int reload) { struct ast_config *cfg; struct ast_variable *v; - struct console_pvt *pvt = &console_pvt; struct ast_flags config_flags = { 0 }; - int res = -1; + char *context = NULL; /* default values */ memcpy(&global_jbconf, &default_jbconf, sizeof(global_jbconf)); - - console_pvt_lock(pvt); - - set_pvt_defaults(pvt, reload); + ast_mutex_lock(&globals_lock); + set_pvt_defaults(&globals); + ast_mutex_unlock(&globals_lock); if (!(cfg = ast_config_load(config_file, config_flags))) { ast_log(LOG_NOTICE, "Unable to open configuration file %s!\n", config_file); - goto return_unlock; + return -1; } + + ao2_callback(pvts, 0, pvt_mark_destroy_cb, NULL); + ast_mutex_lock(&globals_lock); for (v = ast_variable_browse(cfg, "general"); v; v = v->next) - store_config_core(pvt, v->name, v->value); + store_config_core(&globals, v->name, v->value); + ast_mutex_unlock(&globals_lock); + + while ((context = ast_category_browse(cfg, context))) { + if (strcasecmp(context, "general")) + build_device(cfg, context); + } ast_config_destroy(cfg); - res = 0; + destroy_pvts(); -return_unlock: - console_pvt_unlock(pvt); - return res; + return 0; } -static int init_pvt(struct console_pvt *pvt) +static int pvt_hash_cb(const void *obj, const int flags) { - if (ast_string_field_init(pvt, 32)) - return -1; - - if (ast_mutex_init(&pvt->__lock)) { - ast_log(LOG_ERROR, "Failed to initialize mutex\n"); - return -1; - } + const struct console_pvt *pvt = obj; - return 0; + return ast_str_hash(pvt->name); } -static void destroy_pvt(struct console_pvt *pvt) +static int pvt_cmp_cb(void *obj, void *arg, int flags) { - ast_string_field_free_memory(pvt); - - ast_mutex_destroy(&pvt->__lock); + struct console_pvt *pvt = obj, *pvt2 = arg; + + return !strcasecmp(pvt->name, pvt2->name) ? CMP_MATCH : 0; +} + +static void stop_streams(void) +{ + struct console_pvt *pvt; + struct ao2_iterator i; + + i = ao2_iterator_init(pvts, 0); + while ((pvt = ao2_iterator_next(&i))) { + if (pvt->hookstate) + stop_stream(pvt); + unref_pvt(pvt); + } } static int unload_module(void) { - struct console_pvt *pvt = &console_pvt; + ast_channel_unregister(&console_tech); + ast_cli_unregister_multiple(cli_console, ARRAY_LEN(cli_console)); - if (pvt->hookstate) - stop_stream(pvt); + stop_streams(); Pa_Terminate(); - ast_channel_unregister(&console_tech); - ast_cli_unregister_multiple(cli_console, ARRAY_LEN(cli_console)); + /* Will unref all the pvts so they will get destroyed, too */ + ao2_ref(pvts, -1); - destroy_pvt(pvt); + pvt_destructor(&globals); return 0; } @@ -1054,9 +1398,10 @@ static int unload_module(void) static int load_module(void) { PaError res; - struct console_pvt *pvt = &console_pvt; - if (init_pvt(pvt)) + init_pvt(&globals, NULL); + + if (!(pvts = ao2_container_alloc(NUM_PVT_BUCKETS, pvt_hash_cb, pvt_cmp_cb))) goto return_error; if (load_config(0)) @@ -1086,7 +1431,9 @@ return_error_chan_reg: return_error_pa_init: Pa_Terminate(); return_error: - destroy_pvt(pvt); + if (pvts) + ao2_ref(pvts, -1); + pvt_destructor(&globals); return AST_MODULE_LOAD_DECLINE; } |