Wed Oct 28 11:50:56 2009

Asterisk developer's documentation


chan_console.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 2006 - 2008, Digium, Inc.
00005  *
00006  * Russell Bryant <russell@digium.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*! 
00020  * \file 
00021  * \brief Cross-platform console channel driver 
00022  *
00023  * \author Russell Bryant <russell@digium.com>
00024  *
00025  * \note Some of the code in this file came from chan_oss and chan_alsa.
00026  *       chan_oss,  Mark Spencer <markster@digium.com>
00027  *       chan_oss,  Luigi Rizzo
00028  *       chan_alsa, Matthew Fredrickson <creslin@digium.com>
00029  * 
00030  * \ingroup channel_drivers
00031  *
00032  * \extref Portaudio http://www.portaudio.com/
00033  *
00034  * To install portaudio v19 from svn, check it out using the following command:
00035  *  - svn co https://www.portaudio.com/repos/portaudio/branches/v19-devel
00036  *
00037  * \note Since this works with any audio system that libportaudio supports,
00038  * including ALSA and OSS, this may someday deprecate chan_alsa and chan_oss.
00039  * However, before that can be done, it needs to *at least* have all of the
00040  * features that these other channel drivers have.  The features implemented
00041  * in at least one of the other console channel drivers that are not yet
00042  * implemented here are:
00043  *
00044  * - Set Auto-answer from the dialplan
00045  * - transfer CLI command
00046  * - boost CLI command and .conf option
00047  * - console_video support
00048  */
00049 
00050 /*** MODULEINFO
00051    <depend>portaudio</depend>
00052  ***/
00053 
00054 #include "asterisk.h"
00055 
00056 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 222186 $")
00057 
00058 #include <sys/signal.h>  /* SIGURG */
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  * \brief The sample rate to request from PortAudio 
00073  *
00074  * \todo Make this optional.  If this is only going to talk to 8 kHz endpoints,
00075  *       then it makes sense to use 8 kHz natively.
00076  */
00077 #define SAMPLE_RATE      16000
00078 
00079 /*! 
00080  * \brief The number of samples to configure the portaudio stream for
00081  *
00082  * 320 samples (20 ms) is the most common frame size in Asterisk.  So, the code
00083  * in this module reads 320 sample frames from the portaudio stream and queues
00084  * them up on the Asterisk channel.  Frames of any size can be written to a
00085  * portaudio stream, but the portaudio documentation does say that for high
00086  * performance applications, the data should be written to Pa_WriteStream in
00087  * the same size as what is used to initialize the stream.
00088  */
00089 #define NUM_SAMPLES      320
00090 
00091 /*! \brief Mono Input */
00092 #define INPUT_CHANNELS   1
00093 
00094 /*! \brief Mono Output */
00095 #define OUTPUT_CHANNELS  1
00096 
00097 /*! 
00098  * \brief Maximum text message length
00099  * \note This should be changed if there is a common definition somewhere
00100  *       that defines the maximum length of a text message.
00101  */
00102 #define TEXT_SIZE 256
00103 
00104 #ifndef MIN
00105 #define MIN(a,b) ((a) < (b) ? (a) : (b))
00106 #endif
00107 #ifndef MAX
00108 #define MAX(a,b) ((a) > (b) ? (a) : (b))
00109 #endif
00110 
00111 /*! \brief Dance, Kirby, Dance! @{ */
00112 #define V_BEGIN " --- <(\"<) --- "
00113 #define V_END   " --- (>\")> ---\n"
00114 /*! @} */
00115 
00116 static const char config_file[] = "console.conf";
00117 
00118 /*!
00119  * \brief Console pvt structure
00120  *
00121  * Currently, this is a singleton object.  However, multiple instances will be
00122  * needed when this module is updated for multiple device support.
00123  */
00124 static struct console_pvt {
00125    AST_DECLARE_STRING_FIELDS(
00126       /*! Name of the device */
00127       AST_STRING_FIELD(name);
00128       AST_STRING_FIELD(input_device);
00129       AST_STRING_FIELD(output_device);
00130       /*! Default context for outgoing calls */
00131       AST_STRING_FIELD(context);
00132       /*! Default extension for outgoing calls */
00133       AST_STRING_FIELD(exten);
00134       /*! Default CallerID number */
00135       AST_STRING_FIELD(cid_num);
00136       /*! Default CallerID name */
00137       AST_STRING_FIELD(cid_name);
00138       /*! Default MOH class to listen to, if:
00139        *    - No MOH class set on the channel
00140        *    - Peer channel putting this device on hold did not suggest a class */
00141       AST_STRING_FIELD(mohinterpret);
00142       /*! Default language */
00143       AST_STRING_FIELD(language);
00144       /*! Default parkinglot */
00145       AST_STRING_FIELD(parkinglot);
00146    );
00147    /*! Current channel for this device */
00148    struct ast_channel *owner;
00149    /*! Current PortAudio stream for this device */
00150    PaStream *stream;
00151    /*! A frame for preparing to queue on to the channel */
00152    struct ast_frame fr;
00153    /*! Running = 1, Not running = 0 */
00154    unsigned int streamstate:1;
00155    /*! On-hook = 0, Off-hook = 1 */
00156    unsigned int hookstate:1;
00157    /*! Unmuted = 0, Muted = 1 */
00158    unsigned int muted:1;
00159    /*! Automatically answer incoming calls */
00160    unsigned int autoanswer:1;
00161    /*! Ignore context in the console dial CLI command */
00162    unsigned int overridecontext:1;
00163    /*! Set during a reload so that we know to destroy this if it is no longer
00164     *  in the configuration file. */
00165    unsigned int destroy:1;
00166    /*! ID for the stream monitor thread */
00167    pthread_t thread;
00168 } globals;
00169 
00170 AST_MUTEX_DEFINE_STATIC(globals_lock);
00171 
00172 static struct ao2_container *pvts;
00173 #define NUM_PVT_BUCKETS 7
00174 
00175 static struct console_pvt *active_pvt;
00176 AST_RWLOCK_DEFINE_STATIC(active_lock);
00177 
00178 /*! 
00179  * \brief Global jitterbuffer configuration 
00180  *
00181  * \note Disabled by default.
00182  */
00183 static struct ast_jb_conf default_jbconf = {
00184    .flags = 0,
00185    .max_size = -1,
00186    .resync_threshold = -1,
00187    .impl = ""
00188 };
00189 static struct ast_jb_conf global_jbconf;
00190 
00191 /*! Channel Technology Callbacks @{ */
00192 static struct ast_channel *console_request(const char *type, int format, 
00193    void *data, int *cause);
00194 static int console_digit_begin(struct ast_channel *c, char digit);
00195 static int console_digit_end(struct ast_channel *c, char digit, unsigned int duration);
00196 static int console_text(struct ast_channel *c, const char *text);
00197 static int console_hangup(struct ast_channel *c);
00198 static int console_answer(struct ast_channel *c);
00199 static struct ast_frame *console_read(struct ast_channel *chan);
00200 static int console_call(struct ast_channel *c, char *dest, int timeout);
00201 static int console_write(struct ast_channel *chan, struct ast_frame *f);
00202 static int console_indicate(struct ast_channel *chan, int cond, 
00203    const void *data, size_t datalen);
00204 static int console_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
00205 /*! @} */
00206 
00207 /*!
00208  * \brief Formats natively supported by this module.
00209  */
00210 #define SUPPORTED_FORMATS ( AST_FORMAT_SLINEAR16 )
00211 
00212 static const struct ast_channel_tech console_tech = {
00213    .type = "Console",
00214    .description = "Console Channel Driver",
00215    .capabilities = SUPPORTED_FORMATS,
00216    .requester = console_request,
00217    .send_digit_begin = console_digit_begin,
00218    .send_digit_end = console_digit_end,
00219    .send_text = console_text,
00220    .hangup = console_hangup,
00221    .answer = console_answer,
00222    .read = console_read,
00223    .call = console_call,
00224    .write = console_write,
00225    .indicate = console_indicate,
00226    .fixup = console_fixup,
00227 };
00228 
00229 /*! \brief lock a console_pvt struct */
00230 #define console_pvt_lock(pvt) ao2_lock(pvt)
00231 
00232 /*! \brief unlock a console_pvt struct */
00233 #define console_pvt_unlock(pvt) ao2_unlock(pvt)
00234 
00235 static inline struct console_pvt *ref_pvt(struct console_pvt *pvt)
00236 {
00237    if (pvt)
00238       ao2_ref(pvt, +1);
00239    return pvt;
00240 }
00241 
00242 static inline struct console_pvt *unref_pvt(struct console_pvt *pvt)
00243 {
00244    ao2_ref(pvt, -1);
00245    return NULL;
00246 }
00247 
00248 static struct console_pvt *find_pvt(const char *name)
00249 {
00250    struct console_pvt tmp_pvt = {
00251       .name = name,
00252    };
00253 
00254    return ao2_find(pvts, &tmp_pvt, OBJ_POINTER);
00255 }
00256 
00257 /*!
00258  * \brief Stream monitor thread 
00259  *
00260  * \arg data A pointer to the console_pvt structure that contains the portaudio
00261  *      stream that needs to be monitored.
00262  *
00263  * This function runs in its own thread to monitor data coming in from a
00264  * portaudio stream.  When enough data is available, it is queued up to
00265  * be read from the Asterisk channel.
00266  */
00267 static void *stream_monitor(void *data)
00268 {
00269    struct console_pvt *pvt = data;
00270    char buf[NUM_SAMPLES * sizeof(int16_t)];
00271    PaError res;
00272    struct ast_frame f = {
00273       .frametype = AST_FRAME_VOICE,
00274       .subclass = AST_FORMAT_SLINEAR16,
00275       .src = "console_stream_monitor",
00276       .data.ptr = buf,
00277       .datalen = sizeof(buf),
00278       .samples = sizeof(buf) / sizeof(int16_t),
00279    };
00280 
00281    for (;;) {
00282       pthread_testcancel();
00283       res = Pa_ReadStream(pvt->stream, buf, sizeof(buf) / sizeof(int16_t));
00284       pthread_testcancel();
00285 
00286       if (res == paNoError)
00287          ast_queue_frame(pvt->owner, &f);
00288    }
00289 
00290    return NULL;
00291 }
00292 
00293 static int open_stream(struct console_pvt *pvt)
00294 {
00295    int res = paInternalError;
00296 
00297    if (!strcasecmp(pvt->input_device, "default") && 
00298       !strcasecmp(pvt->output_device, "default")) {
00299       res = Pa_OpenDefaultStream(&pvt->stream, INPUT_CHANNELS, OUTPUT_CHANNELS, 
00300          paInt16, SAMPLE_RATE, NUM_SAMPLES, NULL, NULL);
00301    } else {
00302       PaStreamParameters input_params = { 
00303          .channelCount = 1,
00304          .sampleFormat = paInt16,
00305          .suggestedLatency = (1.0 / 50.0), /* 20 ms */
00306          .device = paNoDevice,
00307       };
00308       PaStreamParameters output_params = { 
00309          .channelCount = 1, 
00310          .sampleFormat = paInt16,
00311          .suggestedLatency = (1.0 / 50.0), /* 20 ms */
00312          .device = paNoDevice,
00313       };
00314       PaDeviceIndex idx, num_devices, def_input, def_output;
00315 
00316       if (!(num_devices = Pa_GetDeviceCount()))
00317          return res;
00318 
00319       def_input = Pa_GetDefaultInputDevice();
00320       def_output = Pa_GetDefaultOutputDevice();
00321 
00322       for (idx = 0; 
00323          idx < num_devices && (input_params.device == paNoDevice 
00324             || output_params.device == paNoDevice); 
00325          idx++) 
00326       {
00327          const PaDeviceInfo *dev = Pa_GetDeviceInfo(idx);
00328 
00329          if (dev->maxInputChannels) {
00330             if ( (idx == def_input && !strcasecmp(pvt->input_device, "default")) ||
00331                !strcasecmp(pvt->input_device, dev->name) )
00332                input_params.device = idx;
00333          }
00334 
00335          if (dev->maxOutputChannels) {
00336             if ( (idx == def_output && !strcasecmp(pvt->output_device, "default")) ||
00337                !strcasecmp(pvt->output_device, dev->name) )
00338                output_params.device = idx;
00339          }
00340       }
00341 
00342       if (input_params.device == paNoDevice)
00343          ast_log(LOG_ERROR, "No input device found for console device '%s'\n", pvt->name);
00344       if (output_params.device == paNoDevice)
00345          ast_log(LOG_ERROR, "No output device found for console device '%s'\n", pvt->name);
00346 
00347       res = Pa_OpenStream(&pvt->stream, &input_params, &output_params,
00348          SAMPLE_RATE, NUM_SAMPLES, paNoFlag, NULL, NULL);
00349    }
00350 
00351    return res;
00352 }
00353 
00354 static int start_stream(struct console_pvt *pvt)
00355 {
00356    PaError res;
00357    int ret_val = 0;
00358 
00359    console_pvt_lock(pvt);
00360 
00361    if (pvt->streamstate)
00362       goto return_unlock;
00363 
00364    pvt->streamstate = 1;
00365    ast_debug(1, "Starting stream\n");
00366 
00367    res = open_stream(pvt);
00368    if (res != paNoError) {
00369       ast_log(LOG_WARNING, "Failed to open stream - (%d) %s\n",
00370          res, Pa_GetErrorText(res));
00371       ret_val = -1;
00372       goto return_unlock;
00373    }
00374 
00375    res = Pa_StartStream(pvt->stream);
00376    if (res != paNoError) {
00377       ast_log(LOG_WARNING, "Failed to start stream - (%d) %s\n",
00378          res, Pa_GetErrorText(res));
00379       ret_val = -1;
00380       goto return_unlock;
00381    }
00382 
00383    if (ast_pthread_create_background(&pvt->thread, NULL, stream_monitor, pvt)) {
00384       ast_log(LOG_ERROR, "Failed to start stream monitor thread\n");
00385       ret_val = -1;
00386    }
00387 
00388 return_unlock:
00389    console_pvt_unlock(pvt);
00390 
00391    return ret_val;
00392 }
00393 
00394 static int stop_stream(struct console_pvt *pvt)
00395 {
00396    if (!pvt->streamstate || pvt->thread == AST_PTHREADT_NULL)
00397       return 0;
00398 
00399    pthread_cancel(pvt->thread);
00400    pthread_kill(pvt->thread, SIGURG);
00401    pthread_join(pvt->thread, NULL);
00402 
00403    console_pvt_lock(pvt);
00404    Pa_AbortStream(pvt->stream);
00405    Pa_CloseStream(pvt->stream);
00406    pvt->stream = NULL;
00407    pvt->streamstate = 0;
00408    console_pvt_unlock(pvt);
00409 
00410    return 0;
00411 }
00412 
00413 /*!
00414  * \note Called with the pvt struct locked
00415  */
00416 static struct ast_channel *console_new(struct console_pvt *pvt, const char *ext, const char *ctx, int state)
00417 {
00418    struct ast_channel *chan;
00419 
00420    if (!(chan = ast_channel_alloc(1, state, pvt->cid_num, pvt->cid_name, NULL, 
00421       ext, ctx, 0, "Console/%s", pvt->name))) {
00422       return NULL;
00423    }
00424 
00425    chan->tech = &console_tech;
00426    chan->nativeformats = AST_FORMAT_SLINEAR16;
00427    chan->readformat = AST_FORMAT_SLINEAR16;
00428    chan->writeformat = AST_FORMAT_SLINEAR16;
00429    chan->tech_pvt = ref_pvt(pvt);
00430 
00431    pvt->owner = chan;
00432 
00433    if (!ast_strlen_zero(pvt->language))
00434       ast_string_field_set(chan, language, pvt->language);
00435 
00436    ast_jb_configure(chan, &global_jbconf);
00437 
00438    if (state != AST_STATE_DOWN) {
00439       if (ast_pbx_start(chan)) {
00440          chan->hangupcause = AST_CAUSE_SWITCH_CONGESTION;
00441          ast_hangup(chan);
00442          chan = NULL;
00443       } else
00444          start_stream(pvt);
00445    }
00446 
00447    return chan;
00448 }
00449 
00450 static struct ast_channel *console_request(const char *type, int format, void *data, int *cause)
00451 {
00452    int oldformat = format;
00453    struct ast_channel *chan = NULL;
00454    struct console_pvt *pvt;
00455 
00456    if (!(pvt = find_pvt(data))) {
00457       ast_log(LOG_ERROR, "Console device '%s' not found\n", (char *) data);
00458       return NULL;
00459    }
00460 
00461    format &= SUPPORTED_FORMATS;
00462    if (!format) {
00463       ast_log(LOG_NOTICE, "Channel requested with unsupported format(s): '%d'\n", oldformat);
00464       goto return_unref;
00465    }
00466 
00467    if (pvt->owner) {
00468       ast_log(LOG_NOTICE, "Console channel already active!\n");
00469       *cause = AST_CAUSE_BUSY;
00470       goto return_unref;
00471    }
00472 
00473    console_pvt_lock(pvt);
00474    chan = console_new(pvt, NULL, NULL, AST_STATE_DOWN);
00475    console_pvt_unlock(pvt);
00476 
00477    if (!chan)
00478       ast_log(LOG_WARNING, "Unable to create new Console channel!\n");
00479 
00480 return_unref:
00481    unref_pvt(pvt);
00482 
00483    return chan;
00484 }
00485 
00486 static int console_digit_begin(struct ast_channel *c, char digit)
00487 {
00488    ast_verb(1, V_BEGIN "Console Received Beginning of Digit %c" V_END, digit);
00489 
00490    return -1; /* non-zero to request inband audio */
00491 }
00492 
00493 static int console_digit_end(struct ast_channel *c, char digit, unsigned int duration)
00494 {
00495    ast_verb(1, V_BEGIN "Console Received End of Digit %c (duration %u)" V_END, 
00496       digit, duration);
00497 
00498    return -1; /* non-zero to request inband audio */
00499 }
00500 
00501 static int console_text(struct ast_channel *c, const char *text)
00502 {
00503    ast_verb(1, V_BEGIN "Console Received Text '%s'" V_END, text);
00504 
00505    return 0;
00506 }
00507 
00508 static int console_hangup(struct ast_channel *c)
00509 {
00510    struct console_pvt *pvt = c->tech_pvt;
00511 
00512    ast_verb(1, V_BEGIN "Hangup on Console" V_END);
00513 
00514    pvt->hookstate = 0;
00515    pvt->owner = NULL;
00516    stop_stream(pvt);
00517 
00518    c->tech_pvt = unref_pvt(pvt);
00519 
00520    return 0;
00521 }
00522 
00523 static int console_answer(struct ast_channel *c)
00524 {
00525    struct console_pvt *pvt = c->tech_pvt;
00526 
00527    ast_verb(1, V_BEGIN "Call from Console has been Answered" V_END);
00528 
00529    ast_setstate(c, AST_STATE_UP);
00530 
00531    return start_stream(pvt);
00532 }
00533 
00534 /*
00535  * \brief Implementation of the ast_channel_tech read() callback
00536  *
00537  * Calling this function is harmless.  However, if it does get called, it
00538  * is an indication that something weird happened that really shouldn't
00539  * have and is worth looking into.
00540  * 
00541  * Why should this function not get called?  Well, let me explain.  There are
00542  * a couple of ways to pass on audio that has come from this channel.  The way
00543  * that this channel driver uses is that once the audio is available, it is
00544  * wrapped in an ast_frame and queued onto the channel using ast_queue_frame().
00545  *
00546  * The other method would be signalling to the core that there is audio waiting,
00547  * and that it needs to call the channel's read() callback to get it.  The way
00548  * the channel gets signalled is that one or more file descriptors are placed
00549  * in the fds array on the ast_channel which the core will poll() on.  When the
00550  * fd indicates that input is available, the read() callback is called.  This
00551  * is especially useful when there is a dedicated file descriptor where the
00552  * audio is read from.  An example would be the socket for an RTP stream.
00553  */
00554 static struct ast_frame *console_read(struct ast_channel *chan)
00555 {
00556    ast_debug(1, "I should not be called ...\n");
00557 
00558    return &ast_null_frame;
00559 }
00560 
00561 static int console_call(struct ast_channel *c, char *dest, int timeout)
00562 {
00563    struct ast_frame f = { 0, };
00564    struct console_pvt *pvt = c->tech_pvt;
00565 
00566    ast_verb(1, V_BEGIN "Call to device '%s' on console from '%s' <%s>" V_END,
00567       dest, c->cid.cid_name, c->cid.cid_num);
00568 
00569    console_pvt_lock(pvt);
00570 
00571    if (pvt->autoanswer) {
00572       pvt->hookstate = 1;
00573       console_pvt_unlock(pvt);
00574       ast_verb(1, V_BEGIN "Auto-answered" V_END);
00575       f.frametype = AST_FRAME_CONTROL;
00576       f.subclass = AST_CONTROL_ANSWER;
00577    } else {
00578       console_pvt_unlock(pvt);
00579       ast_verb(1, V_BEGIN "Type 'console answer' to answer, or use the 'autoanswer' option "
00580             "for future calls" V_END);
00581       f.frametype = AST_FRAME_CONTROL;
00582       f.subclass = AST_CONTROL_RINGING;
00583       ast_indicate(c, AST_CONTROL_RINGING);
00584    }
00585 
00586    ast_queue_frame(c, &f);
00587 
00588    return start_stream(pvt);
00589 }
00590 
00591 static int console_write(struct ast_channel *chan, struct ast_frame *f)
00592 {
00593    struct console_pvt *pvt = chan->tech_pvt;
00594 
00595    Pa_WriteStream(pvt->stream, f->data.ptr, f->samples);
00596 
00597    return 0;
00598 }
00599 
00600 static int console_indicate(struct ast_channel *chan, int cond, const void *data, size_t datalen)
00601 {
00602    struct console_pvt *pvt = chan->tech_pvt;
00603    int res = 0;
00604 
00605    switch (cond) {
00606    case AST_CONTROL_BUSY:
00607    case AST_CONTROL_CONGESTION:
00608    case AST_CONTROL_RINGING:
00609    case -1:
00610       res = -1;  /* Ask for inband indications */
00611       break;
00612    case AST_CONTROL_PROGRESS:
00613    case AST_CONTROL_PROCEEDING:
00614    case AST_CONTROL_VIDUPDATE:
00615    case AST_CONTROL_SRCUPDATE:
00616       break;
00617    case AST_CONTROL_HOLD:
00618       ast_verb(1, V_BEGIN "Console Has Been Placed on Hold" V_END);
00619       ast_moh_start(chan, data, pvt->mohinterpret);
00620       break;
00621    case AST_CONTROL_UNHOLD:
00622       ast_verb(1, V_BEGIN "Console Has Been Retrieved from Hold" V_END);
00623       ast_moh_stop(chan);
00624       break;
00625    default:
00626       ast_log(LOG_WARNING, "Don't know how to display condition %d on %s\n", 
00627          cond, chan->name);
00628       /* The core will play inband indications for us if appropriate */
00629       res = -1;
00630    }
00631 
00632    return res;
00633 }
00634 
00635 static int console_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
00636 {
00637    struct console_pvt *pvt = newchan->tech_pvt;
00638 
00639    pvt->owner = newchan;
00640 
00641    return 0;
00642 }
00643 
00644 /*!
00645  * split a string in extension-context, returns pointers to malloc'ed
00646  * strings.
00647  * If we do not have 'overridecontext' then the last @ is considered as
00648  * a context separator, and the context is overridden.
00649  * This is usually not very necessary as you can play with the dialplan,
00650  * and it is nice not to need it because you have '@' in SIP addresses.
00651  * Return value is the buffer address.
00652  *
00653  * \note came from chan_oss
00654  */
00655 static char *ast_ext_ctx(struct console_pvt *pvt, const char *src, char **ext, char **ctx)
00656 {
00657    if (ext == NULL || ctx == NULL)
00658       return NULL;         /* error */
00659 
00660    *ext = *ctx = NULL;
00661 
00662    if (src && *src != '\0')
00663       *ext = ast_strdup(src);
00664 
00665    if (*ext == NULL)
00666       return NULL;
00667 
00668    if (!pvt->overridecontext) {
00669       /* parse from the right */
00670       *ctx = strrchr(*ext, '@');
00671       if (*ctx)
00672          *(*ctx)++ = '\0';
00673    }
00674 
00675    return *ext;
00676 }
00677 
00678 static struct console_pvt *get_active_pvt(void)
00679 {
00680    struct console_pvt *pvt;
00681 
00682    ast_rwlock_rdlock(&active_lock);
00683    pvt = ref_pvt(active_pvt); 
00684    ast_rwlock_unlock(&active_lock);
00685 
00686    return pvt;
00687 }
00688 
00689 static char *cli_console_autoanswer(struct ast_cli_entry *e, int cmd, 
00690    struct ast_cli_args *a)
00691 {
00692    struct console_pvt *pvt = get_active_pvt();
00693    char *res = CLI_SUCCESS;
00694 
00695    switch (cmd) {
00696    case CLI_INIT:
00697       e->command = "console set autoanswer [on|off]";
00698       e->usage =
00699          "Usage: console set autoanswer [on|off]\n"
00700          "       Enables or disables autoanswer feature.  If used without\n"
00701          "       argument, displays the current on/off status of autoanswer.\n"
00702          "       The default value of autoanswer is in 'oss.conf'.\n";
00703       return NULL;
00704 
00705    case CLI_GENERATE:
00706       return NULL;
00707    }
00708 
00709    if (!pvt) {
00710       ast_cli(a->fd, "No console device is set as active.\n");
00711       return CLI_FAILURE;
00712    }
00713 
00714    if (a->argc == e->args - 1) {
00715       ast_cli(a->fd, "Auto answer is %s.\n", pvt->autoanswer ? "on" : "off");
00716       unref_pvt(pvt);
00717       return CLI_SUCCESS;
00718    }
00719 
00720    if (a->argc != e->args) {
00721       unref_pvt(pvt);
00722       return CLI_SHOWUSAGE;
00723    }
00724 
00725    if (!strcasecmp(a->argv[e->args-1], "on"))
00726       pvt->autoanswer = 1;
00727    else if (!strcasecmp(a->argv[e->args - 1], "off"))
00728       pvt->autoanswer = 0;
00729    else
00730       res = CLI_SHOWUSAGE;
00731 
00732    unref_pvt(pvt);
00733 
00734    return CLI_SUCCESS;
00735 }
00736 
00737 static char *cli_console_flash(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00738 {
00739    struct ast_frame f = { AST_FRAME_CONTROL, AST_CONTROL_FLASH };
00740    struct console_pvt *pvt = get_active_pvt();
00741 
00742    if (cmd == CLI_INIT) {
00743       e->command = "console flash";
00744       e->usage =
00745          "Usage: console flash\n"
00746          "       Flashes the call currently placed on the console.\n";
00747       return NULL;
00748    } else if (cmd == CLI_GENERATE)
00749       return NULL;
00750 
00751    if (!pvt) {
00752       ast_cli(a->fd, "No console device is set as active\n");
00753       return CLI_FAILURE;
00754    }
00755 
00756    if (a->argc != e->args)
00757       return CLI_SHOWUSAGE;
00758 
00759    if (!pvt->owner) {
00760       ast_cli(a->fd, "No call to flash\n");
00761       unref_pvt(pvt);
00762       return CLI_FAILURE;
00763    }
00764 
00765    pvt->hookstate = 0;
00766 
00767    ast_queue_frame(pvt->owner, &f);
00768 
00769    unref_pvt(pvt);
00770 
00771    return CLI_SUCCESS;
00772 }
00773 
00774 static char *cli_console_dial(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00775 {
00776    char *s = NULL;
00777    const char *mye = NULL, *myc = NULL; 
00778    struct console_pvt *pvt = get_active_pvt();
00779 
00780    if (cmd == CLI_INIT) {
00781       e->command = "console dial";
00782       e->usage =
00783          "Usage: console dial [extension[@context]]\n"
00784          "       Dials a given extension (and context if specified)\n";
00785       return NULL;
00786    } else if (cmd == CLI_GENERATE)
00787       return NULL;
00788 
00789    if (!pvt) {
00790       ast_cli(a->fd, "No console device is currently set as active\n");
00791       return CLI_FAILURE;
00792    }
00793    
00794    if (a->argc > e->args + 1)
00795       return CLI_SHOWUSAGE;
00796 
00797    if (pvt->owner) { /* already in a call */
00798       int i;
00799       struct ast_frame f = { AST_FRAME_DTMF, 0 };
00800 
00801       if (a->argc == e->args) {  /* argument is mandatory here */
00802          ast_cli(a->fd, "Already in a call. You can only dial digits until you hangup.\n");
00803          unref_pvt(pvt);
00804          return CLI_FAILURE;
00805       }
00806       s = a->argv[e->args];
00807       /* send the string one char at a time */
00808       for (i = 0; i < strlen(s); i++) {
00809          f.subclass = s[i];
00810          ast_queue_frame(pvt->owner, &f);
00811       }
00812       unref_pvt(pvt);
00813       return CLI_SUCCESS;
00814    }
00815 
00816    /* if we have an argument split it into extension and context */
00817    if (a->argc == e->args + 1) {
00818       char *ext = NULL, *con = NULL;
00819       s = ast_ext_ctx(pvt, a->argv[e->args], &ext, &con);
00820       ast_debug(1, "provided '%s', exten '%s' context '%s'\n", 
00821          a->argv[e->args], mye, myc);
00822       mye = ext;
00823       myc = con;
00824    }
00825 
00826    /* supply default values if needed */
00827    if (ast_strlen_zero(mye))
00828       mye = pvt->exten;
00829    if (ast_strlen_zero(myc))
00830       myc = pvt->context;
00831 
00832    if (ast_exists_extension(NULL, myc, mye, 1, NULL)) {
00833       console_pvt_lock(pvt);
00834       pvt->hookstate = 1;
00835       console_new(pvt, mye, myc, AST_STATE_RINGING);
00836       console_pvt_unlock(pvt);
00837    } else
00838       ast_cli(a->fd, "No such extension '%s' in context '%s'\n", mye, myc);
00839 
00840    if (s)
00841       free(s);
00842 
00843    unref_pvt(pvt);
00844 
00845    return CLI_SUCCESS;
00846 }
00847 
00848 static char *cli_console_hangup(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00849 {
00850    struct console_pvt *pvt = get_active_pvt();
00851 
00852    if (cmd == CLI_INIT) {
00853       e->command = "console hangup";
00854       e->usage =
00855          "Usage: console hangup\n"
00856          "       Hangs up any call currently placed on the console.\n";
00857       return NULL;
00858    } else if (cmd == CLI_GENERATE)
00859       return NULL;
00860 
00861    if (!pvt) {
00862       ast_cli(a->fd, "No console device is set as active\n");
00863       return CLI_FAILURE;
00864    }
00865    
00866    if (a->argc != e->args)
00867       return CLI_SHOWUSAGE;
00868 
00869    if (!pvt->owner && !pvt->hookstate) {
00870       ast_cli(a->fd, "No call to hang up\n");
00871       unref_pvt(pvt);
00872       return CLI_FAILURE;
00873    }
00874 
00875    pvt->hookstate = 0;
00876    if (pvt->owner)
00877       ast_queue_hangup(pvt->owner);
00878 
00879    unref_pvt(pvt);
00880 
00881    return CLI_SUCCESS;
00882 }
00883 
00884 static char *cli_console_mute(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00885 {
00886    char *s;
00887    struct console_pvt *pvt = get_active_pvt();
00888    char *res = CLI_SUCCESS;
00889 
00890    if (cmd == CLI_INIT) {
00891       e->command = "console {mute|unmute}";
00892       e->usage =
00893          "Usage: console {mute|unmute}\n"
00894          "       Mute/unmute the microphone.\n";
00895       return NULL;
00896    } else if (cmd == CLI_GENERATE)
00897       return NULL;
00898 
00899    if (!pvt) {
00900       ast_cli(a->fd, "No console device is set as active\n");
00901       return CLI_FAILURE;
00902    }
00903 
00904    if (a->argc != e->args)
00905       return CLI_SHOWUSAGE;
00906 
00907    s = a->argv[e->args-1];
00908    if (!strcasecmp(s, "mute"))
00909       pvt->muted = 1;
00910    else if (!strcasecmp(s, "unmute"))
00911       pvt->muted = 0;
00912    else
00913       res = CLI_SHOWUSAGE;
00914 
00915    ast_verb(1, V_BEGIN "The Console is now %s" V_END, 
00916       pvt->muted ? "Muted" : "Unmuted");
00917 
00918    unref_pvt(pvt);
00919 
00920    return res;
00921 }
00922 
00923 static char *cli_list_available(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00924 {
00925    PaDeviceIndex idx, num, def_input, def_output;
00926 
00927    if (cmd == CLI_INIT) {
00928       e->command = "console list available";
00929       e->usage =
00930          "Usage: console list available\n"
00931          "       List all available devices.\n";
00932       return NULL;
00933    } else if (cmd == CLI_GENERATE)
00934       return NULL;
00935 
00936    if (a->argc != e->args)
00937       return CLI_SHOWUSAGE;
00938 
00939    ast_cli(a->fd, "\n"
00940                "=============================================================\n"
00941                "=== Available Devices =======================================\n"
00942                "=============================================================\n"
00943                "===\n");
00944 
00945    num = Pa_GetDeviceCount();
00946    if (!num) {
00947       ast_cli(a->fd, "(None)\n");
00948       return CLI_SUCCESS;
00949    }
00950 
00951    def_input = Pa_GetDefaultInputDevice();
00952    def_output = Pa_GetDefaultOutputDevice();
00953    for (idx = 0; idx < num; idx++) {
00954       const PaDeviceInfo *dev = Pa_GetDeviceInfo(idx);
00955       if (!dev)
00956          continue;
00957       ast_cli(a->fd, "=== ---------------------------------------------------------\n"
00958                      "=== Device Name: %s\n", dev->name);
00959       if (dev->maxInputChannels)
00960          ast_cli(a->fd, "=== ---> %sInput Device\n", (idx == def_input) ? "Default " : "");
00961       if (dev->maxOutputChannels)
00962          ast_cli(a->fd, "=== ---> %sOutput Device\n", (idx == def_output) ? "Default " : "");
00963       ast_cli(a->fd, "=== ---------------------------------------------------------\n===\n");
00964    }
00965 
00966    ast_cli(a->fd, "=============================================================\n\n");
00967 
00968    return CLI_SUCCESS;
00969 }
00970 
00971 static char *cli_list_devices(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00972 {
00973    struct ao2_iterator i;
00974    struct console_pvt *pvt;
00975 
00976    if (cmd == CLI_INIT) {
00977       e->command = "console list devices";
00978       e->usage =
00979          "Usage: console list devices\n"
00980          "       List all configured devices.\n";
00981       return NULL;
00982    } else if (cmd == CLI_GENERATE)
00983       return NULL;
00984 
00985    if (a->argc != e->args)
00986       return CLI_SHOWUSAGE;
00987 
00988    ast_cli(a->fd, "\n"
00989                "=============================================================\n"
00990                "=== Configured Devices ======================================\n"
00991                "=============================================================\n"
00992                "===\n");
00993 
00994    i = ao2_iterator_init(pvts, 0);
00995    while ((pvt = ao2_iterator_next(&i))) {
00996       console_pvt_lock(pvt);
00997 
00998       ast_cli(a->fd, "=== ---------------------------------------------------------\n"
00999                      "=== Device Name: %s\n"
01000                      "=== ---> Active:           %s\n"
01001                      "=== ---> Input Device:     %s\n"
01002                      "=== ---> Output Device:    %s\n"
01003                      "=== ---> Context:          %s\n"
01004                      "=== ---> Extension:        %s\n"
01005                      "=== ---> CallerID Num:     %s\n"
01006                      "=== ---> CallerID Name:    %s\n"
01007                      "=== ---> MOH Interpret:    %s\n"
01008                      "=== ---> Language:         %s\n"
01009                      "=== ---> Parkinglot:       %s\n"
01010                      "=== ---> Muted:            %s\n"
01011                      "=== ---> Auto-Answer:      %s\n"
01012                      "=== ---> Override Context: %s\n"
01013                      "=== ---------------------------------------------------------\n===\n",
01014          pvt->name, (pvt == active_pvt) ? "Yes" : "No",
01015          pvt->input_device, pvt->output_device, pvt->context,
01016          pvt->exten, pvt->cid_num, pvt->cid_name, pvt->mohinterpret,
01017          pvt->language, pvt->parkinglot, pvt->muted ? "Yes" : "No", pvt->autoanswer ? "Yes" : "No",
01018          pvt->overridecontext ? "Yes" : "No");
01019 
01020       console_pvt_unlock(pvt);
01021       unref_pvt(pvt);
01022    }
01023    ao2_iterator_destroy(&i);
01024 
01025    ast_cli(a->fd, "=============================================================\n\n");
01026 
01027    return CLI_SUCCESS;
01028 }
01029 /*!
01030  * \brief answer command from the console
01031  */
01032 static char *cli_console_answer(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01033 {
01034    struct ast_frame f = { AST_FRAME_CONTROL, AST_CONTROL_ANSWER };
01035    struct console_pvt *pvt = get_active_pvt();
01036 
01037    switch (cmd) {
01038    case CLI_INIT:
01039       e->command = "console answer";
01040       e->usage =
01041          "Usage: console answer\n"
01042          "       Answers an incoming call on the console channel.\n";
01043       return NULL;
01044 
01045    case CLI_GENERATE:
01046       return NULL;   /* no completion */
01047    }
01048 
01049    if (!pvt) {
01050       ast_cli(a->fd, "No console device is set as active\n");
01051       return CLI_FAILURE;
01052    }
01053 
01054    if (a->argc != e->args) {
01055       unref_pvt(pvt);
01056       return CLI_SHOWUSAGE;
01057    }
01058 
01059    if (!pvt->owner) {
01060       ast_cli(a->fd, "No one is calling us\n");
01061       unref_pvt(pvt);
01062       return CLI_FAILURE;
01063    }
01064 
01065    pvt->hookstate = 1;
01066 
01067    ast_indicate(pvt->owner, -1);
01068 
01069    ast_queue_frame(pvt->owner, &f);
01070 
01071    unref_pvt(pvt);
01072 
01073    return CLI_SUCCESS;
01074 }
01075 
01076 /*!
01077  * \brief Console send text CLI command
01078  *
01079  * \note concatenate all arguments into a single string. argv is NULL-terminated
01080  * so we can use it right away
01081  */
01082 static char *cli_console_sendtext(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01083 {
01084    char buf[TEXT_SIZE];
01085    struct console_pvt *pvt = get_active_pvt();
01086    struct ast_frame f = {
01087       .frametype = AST_FRAME_TEXT,
01088       .data.ptr = buf,
01089       .src = "console_send_text",
01090    };
01091    int len;
01092 
01093    if (cmd == CLI_INIT) {
01094       e->command = "console send text";
01095       e->usage =
01096          "Usage: console send text <message>\n"
01097          "       Sends a text message for display on the remote terminal.\n";
01098       return NULL;
01099    } else if (cmd == CLI_GENERATE)
01100       return NULL;
01101 
01102    if (!pvt) {
01103       ast_cli(a->fd, "No console device is set as active\n");
01104       return CLI_FAILURE;
01105    }
01106 
01107    if (a->argc < e->args + 1) {
01108       unref_pvt(pvt);
01109       return CLI_SHOWUSAGE;
01110    }
01111 
01112    if (!pvt->owner) {
01113       ast_cli(a->fd, "Not in a call\n");
01114       unref_pvt(pvt);
01115       return CLI_FAILURE;
01116    }
01117 
01118    ast_join(buf, sizeof(buf) - 1, a->argv + e->args);
01119    if (ast_strlen_zero(buf)) {
01120       unref_pvt(pvt);
01121       return CLI_SHOWUSAGE;
01122    }
01123 
01124    len = strlen(buf);
01125    buf[len] = '\n';
01126    f.datalen = len + 1;
01127 
01128    ast_queue_frame(pvt->owner, &f);
01129 
01130    unref_pvt(pvt);
01131 
01132    return CLI_SUCCESS;
01133 }
01134 
01135 static void set_active(struct console_pvt *pvt, const char *value)
01136 {
01137    if (pvt == &globals) {
01138       ast_log(LOG_ERROR, "active is only valid as a per-device setting\n");
01139       return;
01140    }
01141 
01142    if (!ast_true(value))
01143       return;
01144 
01145    ast_rwlock_wrlock(&active_lock);
01146    if (active_pvt)
01147       unref_pvt(active_pvt);
01148    active_pvt = ref_pvt(pvt);
01149    ast_rwlock_unlock(&active_lock);
01150 }
01151 
01152 static char *cli_console_active(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01153 {
01154    struct console_pvt *pvt;
01155 
01156    switch (cmd) {
01157    case CLI_INIT:
01158       e->command = "console active";
01159       e->usage =
01160          "Usage: console active [device]\n"
01161          "       If no device is specified.  The active console device will be shown.\n"
01162          "Otherwise, the specified device will become the console device active for\n"
01163          "the Asterisk CLI.\n";
01164       return NULL;
01165    case CLI_GENERATE:
01166       if (a->pos == e->args) {
01167          struct ao2_iterator i;
01168          int x = 0;
01169          char *res = NULL;
01170          i = ao2_iterator_init(pvts, 0);
01171          while ((pvt = ao2_iterator_next(&i))) {
01172             if (++x > a->n && !strncasecmp(pvt->name, a->word, strlen(a->word)))
01173                res = ast_strdup(pvt->name);
01174             unref_pvt(pvt);
01175             if (res) {
01176                ao2_iterator_destroy(&i);
01177                return res;
01178             }
01179          }
01180          ao2_iterator_destroy(&i);
01181       }
01182       return NULL;
01183    }
01184 
01185    if (a->argc < e->args)
01186       return CLI_SHOWUSAGE;
01187 
01188    if (a->argc == e->args) {
01189       pvt = get_active_pvt();
01190 
01191       if (!pvt)
01192          ast_cli(a->fd, "No device is currently set as the active console device.\n");
01193       else {
01194          console_pvt_lock(pvt);
01195          ast_cli(a->fd, "The active console device is '%s'.\n", pvt->name);
01196          console_pvt_unlock(pvt);
01197          pvt = unref_pvt(pvt);
01198       }
01199 
01200       return CLI_SUCCESS;
01201    }
01202 
01203    if (!(pvt = find_pvt(a->argv[e->args]))) {
01204       ast_cli(a->fd, "Could not find a device called '%s'.\n", a->argv[e->args]);
01205       return CLI_FAILURE;
01206    }
01207 
01208    set_active(pvt, "yes");
01209 
01210    console_pvt_lock(pvt);
01211    ast_cli(a->fd, "The active console device has been set to '%s'\n", pvt->name);
01212    console_pvt_unlock(pvt);
01213 
01214    unref_pvt(pvt);
01215 
01216    return CLI_SUCCESS;
01217 }
01218 
01219 static struct ast_cli_entry cli_console[] = {
01220    AST_CLI_DEFINE(cli_console_dial,       "Dial an extension from the console"),
01221    AST_CLI_DEFINE(cli_console_hangup,     "Hangup a call on the console"),
01222    AST_CLI_DEFINE(cli_console_mute,       "Disable/Enable mic input"),
01223    AST_CLI_DEFINE(cli_console_answer,     "Answer an incoming console call"),
01224    AST_CLI_DEFINE(cli_console_sendtext,   "Send text to a connected party"),
01225    AST_CLI_DEFINE(cli_console_flash,      "Send a flash to the connected party"),
01226    AST_CLI_DEFINE(cli_console_autoanswer, "Turn autoanswer on or off"),
01227    AST_CLI_DEFINE(cli_list_available,     "List available devices"),
01228    AST_CLI_DEFINE(cli_list_devices,       "List configured devices"),
01229    AST_CLI_DEFINE(cli_console_active,     "View or Set the active console device"),
01230 };
01231 
01232 /*!
01233  * \brief Set default values for a pvt struct
01234  *
01235  * \note This function expects the pvt lock to be held.
01236  */
01237 static void set_pvt_defaults(struct console_pvt *pvt)
01238 {
01239    if (pvt == &globals) {
01240       ast_string_field_set(pvt, mohinterpret, "default");
01241       ast_string_field_set(pvt, context, "default");
01242       ast_string_field_set(pvt, exten, "s");
01243       ast_string_field_set(pvt, language, "");
01244       ast_string_field_set(pvt, cid_num, "");
01245       ast_string_field_set(pvt, cid_name, "");
01246       ast_string_field_set(pvt, parkinglot, "");
01247    
01248       pvt->overridecontext = 0;
01249       pvt->autoanswer = 0;
01250    } else {
01251       ast_mutex_lock(&globals_lock);
01252 
01253       ast_string_field_set(pvt, mohinterpret, globals.mohinterpret);
01254       ast_string_field_set(pvt, context, globals.context);
01255       ast_string_field_set(pvt, exten, globals.exten);
01256       ast_string_field_set(pvt, language, globals.language);
01257       ast_string_field_set(pvt, cid_num, globals.cid_num);
01258       ast_string_field_set(pvt, cid_name, globals.cid_name);
01259       ast_string_field_set(pvt, parkinglot, globals.parkinglot);
01260 
01261       pvt->overridecontext = globals.overridecontext;
01262       pvt->autoanswer = globals.autoanswer;
01263 
01264       ast_mutex_unlock(&globals_lock);
01265    }
01266 }
01267 
01268 static void store_callerid(struct console_pvt *pvt, const char *value)
01269 {
01270    char cid_name[256];
01271    char cid_num[256];
01272 
01273    ast_callerid_split(value, cid_name, sizeof(cid_name), 
01274       cid_num, sizeof(cid_num));
01275 
01276    ast_string_field_set(pvt, cid_name, cid_name);
01277    ast_string_field_set(pvt, cid_num, cid_num);
01278 }
01279 
01280 /*!
01281  * \brief Store a configuration parameter in a pvt struct
01282  *
01283  * \note This function expects the pvt lock to be held.
01284  */
01285 static void store_config_core(struct console_pvt *pvt, const char *var, const char *value)
01286 {
01287    if (pvt == &globals && !ast_jb_read_conf(&global_jbconf, var, value))
01288       return;
01289 
01290    CV_START(var, value);
01291 
01292    CV_STRFIELD("context", pvt, context);
01293    CV_STRFIELD("extension", pvt, exten);
01294    CV_STRFIELD("mohinterpret", pvt, mohinterpret);
01295    CV_STRFIELD("language", pvt, language);
01296    CV_F("callerid", store_callerid(pvt, value));
01297    CV_BOOL("overridecontext", pvt->overridecontext);
01298    CV_BOOL("autoanswer", pvt->autoanswer);
01299    CV_STRFIELD("parkinglot", pvt, parkinglot);
01300 
01301    if (pvt != &globals) {
01302       CV_F("active", set_active(pvt, value))
01303       CV_STRFIELD("input_device", pvt, input_device);
01304       CV_STRFIELD("output_device", pvt, output_device);
01305    }
01306 
01307    ast_log(LOG_WARNING, "Unknown option '%s'\n", var);
01308 
01309    CV_END;
01310 }
01311 
01312 static void pvt_destructor(void *obj)
01313 {
01314    struct console_pvt *pvt = obj;
01315 
01316    ast_string_field_free_memory(pvt);
01317 }
01318 
01319 static int init_pvt(struct console_pvt *pvt, const char *name)
01320 {
01321    pvt->thread = AST_PTHREADT_NULL;
01322 
01323    if (ast_string_field_init(pvt, 32))
01324       return -1;
01325 
01326    ast_string_field_set(pvt, name, S_OR(name, ""));
01327 
01328    return 0;
01329 }
01330 
01331 static void build_device(struct ast_config *cfg, const char *name)
01332 {
01333    struct ast_variable *v;
01334    struct console_pvt *pvt;
01335    int new = 0;
01336 
01337    if ((pvt = find_pvt(name))) {
01338       console_pvt_lock(pvt);
01339       set_pvt_defaults(pvt);
01340       pvt->destroy = 0;
01341    } else {
01342       if (!(pvt = ao2_alloc(sizeof(*pvt), pvt_destructor)))
01343          return;
01344       init_pvt(pvt, name);
01345       set_pvt_defaults(pvt);
01346       new = 1;
01347    }
01348 
01349    for (v = ast_variable_browse(cfg, name); v; v = v->next)
01350       store_config_core(pvt, v->name, v->value);
01351 
01352    if (new)
01353       ao2_link(pvts, pvt);
01354    else
01355       console_pvt_unlock(pvt);
01356    
01357    unref_pvt(pvt);
01358 }
01359 
01360 static int pvt_mark_destroy_cb(void *obj, void *arg, int flags)
01361 {
01362    struct console_pvt *pvt = obj;
01363    pvt->destroy = 1;
01364    return 0;
01365 }
01366 
01367 static void destroy_pvts(void)
01368 {
01369    struct ao2_iterator i;
01370    struct console_pvt *pvt;
01371 
01372    i = ao2_iterator_init(pvts, 0);
01373    while ((pvt = ao2_iterator_next(&i))) {
01374       if (pvt->destroy) {
01375          ao2_unlink(pvts, pvt);
01376          ast_rwlock_wrlock(&active_lock);
01377          if (active_pvt == pvt)
01378             active_pvt = unref_pvt(pvt);
01379          ast_rwlock_unlock(&active_lock);
01380       }
01381       unref_pvt(pvt);
01382    }
01383    ao2_iterator_destroy(&i);
01384 }
01385 
01386 /*!
01387  * \brief Load the configuration
01388  * \param reload if this was called due to a reload
01389  * \retval 0 success
01390  * \retval -1 failure
01391  */
01392 static int load_config(int reload)
01393 {
01394    struct ast_config *cfg;
01395    struct ast_variable *v;
01396    struct ast_flags config_flags = { 0 };
01397    char *context = NULL;
01398 
01399    /* default values */
01400    memcpy(&global_jbconf, &default_jbconf, sizeof(global_jbconf));
01401    ast_mutex_lock(&globals_lock);
01402    set_pvt_defaults(&globals);
01403    ast_mutex_unlock(&globals_lock);
01404 
01405    if (!(cfg = ast_config_load(config_file, config_flags))) {
01406       ast_log(LOG_NOTICE, "Unable to open configuration file %s!\n", config_file);
01407       return -1;
01408    }
01409    
01410    ao2_callback(pvts, OBJ_NODATA, pvt_mark_destroy_cb, NULL);
01411 
01412    ast_mutex_lock(&globals_lock);
01413    for (v = ast_variable_browse(cfg, "general"); v; v = v->next)
01414       store_config_core(&globals, v->name, v->value);
01415    ast_mutex_unlock(&globals_lock);
01416 
01417    while ((context = ast_category_browse(cfg, context))) {
01418       if (strcasecmp(context, "general"))
01419          build_device(cfg, context);
01420    }
01421 
01422    ast_config_destroy(cfg);
01423 
01424    destroy_pvts();
01425 
01426    return 0;
01427 }
01428 
01429 static int pvt_hash_cb(const void *obj, const int flags)
01430 {
01431    const struct console_pvt *pvt = obj;
01432 
01433    return ast_str_case_hash(pvt->name);
01434 }
01435 
01436 static int pvt_cmp_cb(void *obj, void *arg, int flags)
01437 {
01438    struct console_pvt *pvt = obj, *pvt2 = arg;
01439 
01440    return !strcasecmp(pvt->name, pvt2->name) ? CMP_MATCH | CMP_STOP : 0;
01441 }
01442 
01443 static void stop_streams(void)
01444 {
01445    struct console_pvt *pvt;
01446    struct ao2_iterator i;
01447 
01448    i = ao2_iterator_init(pvts, 0);
01449    while ((pvt = ao2_iterator_next(&i))) {
01450       if (pvt->hookstate)
01451          stop_stream(pvt);
01452       unref_pvt(pvt);
01453    }
01454    ao2_iterator_destroy(&i);
01455 }
01456 
01457 static int unload_module(void)
01458 {
01459    ast_channel_unregister(&console_tech);
01460    ast_cli_unregister_multiple(cli_console, ARRAY_LEN(cli_console));
01461 
01462    stop_streams();
01463 
01464    Pa_Terminate();
01465 
01466    /* Will unref all the pvts so they will get destroyed, too */
01467    ao2_ref(pvts, -1);
01468 
01469    pvt_destructor(&globals);
01470 
01471    return 0;
01472 }
01473 
01474 static int load_module(void)
01475 {
01476    PaError res;
01477 
01478    init_pvt(&globals, NULL);
01479 
01480    if (!(pvts = ao2_container_alloc(NUM_PVT_BUCKETS, pvt_hash_cb, pvt_cmp_cb)))
01481       goto return_error;
01482 
01483    if (load_config(0))
01484       goto return_error;
01485 
01486    res = Pa_Initialize();
01487    if (res != paNoError) {
01488       ast_log(LOG_WARNING, "Failed to initialize audio system - (%d) %s\n",
01489          res, Pa_GetErrorText(res));
01490       goto return_error_pa_init;
01491    }
01492 
01493    if (ast_channel_register(&console_tech)) {
01494       ast_log(LOG_ERROR, "Unable to register channel type 'Console'\n");
01495       goto return_error_chan_reg;
01496    }
01497 
01498    if (ast_cli_register_multiple(cli_console, ARRAY_LEN(cli_console)))
01499       goto return_error_cli_reg;
01500 
01501    return AST_MODULE_LOAD_SUCCESS;
01502 
01503 return_error_cli_reg:
01504    ast_cli_unregister_multiple(cli_console, ARRAY_LEN(cli_console));
01505 return_error_chan_reg:
01506    ast_channel_unregister(&console_tech);
01507 return_error_pa_init:
01508    Pa_Terminate();
01509 return_error:
01510    if (pvts)
01511       ao2_ref(pvts, -1);
01512    pvt_destructor(&globals);
01513 
01514    return AST_MODULE_LOAD_DECLINE;
01515 }
01516 
01517 static int reload(void)
01518 {
01519    return load_config(1);
01520 }
01521 
01522 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Console Channel Driver",
01523       .load = load_module,
01524       .unload = unload_module,
01525       .reload = reload,
01526 );

Generated on Wed Oct 28 11:50:56 2009 for Asterisk - the Open Source PBX by  doxygen 1.5.6