core: Don't allow free to mean ast_free (and malloc, etc..).
[asterisk/asterisk.git] / channels / chan_console.c
index c7090a1..7ddbcf0 100644 (file)
@@ -29,7 +29,7 @@
  * 
  * \ingroup channel_drivers
  *
- * \extref Portaudio http://www.portaudio.com/
+ * Portaudio http://www.portaudio.com/
  *
  * To install portaudio v19 from svn, check it out using the following command:
  *  - svn co https://www.portaudio.com/repos/portaudio/branches/v19-devel
  * - console_video support
  */
 
+/*! \li \ref chan_console.c uses the configuration file \ref console.conf
+ * \addtogroup configuration_file
+ */
+
+/*! \page console.conf console.conf
+ * \verbinclude console.conf.sample
+ */
+
 /*** MODULEINFO
        <depend>portaudio</depend>
+       <support_level>extended</support_level>
  ***/
 
 #include "asterisk.h"
@@ -67,6 +76,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/musiconhold.h"
 #include "asterisk/callerid.h"
 #include "asterisk/astobj2.h"
+#include "asterisk/stasis_channels.h"
+#include "asterisk/format_cache.h"
 
 /*! 
  * \brief The sample rate to request from PortAudio 
@@ -101,13 +112,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
  */
 #define TEXT_SIZE      256
 
-#ifndef MIN
-#define MIN(a,b) ((a) < (b) ? (a) : (b))
-#endif
-#ifndef MAX
-#define MAX(a,b) ((a) > (b) ? (a) : (b))
-#endif
-
 /*! \brief Dance, Kirby, Dance! @{ */
 #define V_BEGIN " --- <(\"<) --- "
 #define V_END   " --- (>\")> ---\n"
@@ -179,40 +183,36 @@ AST_RWLOCK_DEFINE_STATIC(active_lock);
  * \brief Global jitterbuffer configuration 
  *
  * \note Disabled by default.
+ * \note Values shown here match the defaults shown in console.conf.sample
  */
 static struct ast_jb_conf default_jbconf = {
        .flags = 0,
-       .max_size = -1,
-       .resync_threshold = -1,
-       .impl = ""
+       .max_size = 200,
+       .resync_threshold = 1000,
+       .impl = "fixed",
+       .target_extra = 40,
 };
 static struct ast_jb_conf global_jbconf;
 
 /*! Channel Technology Callbacks @{ */
-static struct ast_channel *console_request(const char *type, int format, 
-       void *data, int *cause);
+static struct ast_channel *console_request(const char *type, struct ast_format_cap *cap,
+       const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *data, int *cause);
 static int console_digit_begin(struct ast_channel *c, char digit);
 static int console_digit_end(struct ast_channel *c, char digit, unsigned int duration);
 static int console_text(struct ast_channel *c, const char *text);
 static int console_hangup(struct ast_channel *c);
 static int console_answer(struct ast_channel *c);
 static struct ast_frame *console_read(struct ast_channel *chan);
-static int console_call(struct ast_channel *c, char *dest, int timeout);
+static int console_call(struct ast_channel *c, const char *dest, int timeout);
 static int console_write(struct ast_channel *chan, struct ast_frame *f);
 static int console_indicate(struct ast_channel *chan, int cond, 
        const void *data, size_t datalen);
 static int console_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
 /*! @} */
 
-/*!
- * \brief Formats natively supported by this module.
- */
-#define SUPPORTED_FORMATS ( AST_FORMAT_SLINEAR16 )
-
-static const struct ast_channel_tech console_tech = {
+static struct ast_channel_tech console_tech = {
        .type = "Console",
        .description = "Console Channel Driver",
-       .capabilities = SUPPORTED_FORMATS,
        .requester = console_request,
        .send_digit_begin = console_digit_begin,
        .send_digit_end = console_digit_end,
@@ -271,7 +271,7 @@ static void *stream_monitor(void *data)
        PaError res;
        struct ast_frame f = {
                .frametype = AST_FRAME_VOICE,
-               .subclass = AST_FORMAT_SLINEAR16,
+               .subclass.format = ast_format_slin16,
                .src = "console_stream_monitor",
                .data.ptr = buf,
                .datalen = sizeof(buf),
@@ -283,6 +283,10 @@ static void *stream_monitor(void *data)
                res = Pa_ReadStream(pvt->stream, buf, sizeof(buf) / sizeof(int16_t));
                pthread_testcancel();
 
+               if (!pvt->owner) {
+                       return NULL;
+               }
+
                if (res == paNoError)
                        ast_queue_frame(pvt->owner, &f);
        }
@@ -358,7 +362,10 @@ static int start_stream(struct console_pvt *pvt)
 
        console_pvt_lock(pvt);
 
-       if (pvt->streamstate)
+       /* It is possible for console_hangup to be called before the
+        * stream is started, if this is the case pvt->owner will be NULL
+        * and start_stream should be aborted. */
+       if (pvt->streamstate || !pvt->owner)
                goto return_unlock;
 
        pvt->streamstate = 1;
@@ -413,31 +420,45 @@ static int stop_stream(struct console_pvt *pvt)
 /*!
  * \note Called with the pvt struct locked
  */
-static struct ast_channel *console_new(struct console_pvt *pvt, const char *ext, const char *ctx, int state)
+static struct ast_channel *console_new(struct console_pvt *pvt, const char *ext, const char *ctx, int state, const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor)
 {
+       struct ast_format_cap *caps;
        struct ast_channel *chan;
 
+       caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
+       if (!caps) {
+               return NULL;
+       }
+
        if (!(chan = ast_channel_alloc(1, state, pvt->cid_num, pvt->cid_name, NULL, 
-               ext, ctx, 0, "Console/%s", pvt->name))) {
+               ext, ctx, assignedids, requestor, 0, "Console/%s", pvt->name))) {
+               ao2_ref(caps, -1);
                return NULL;
        }
 
-       chan->tech = &console_tech;
-       chan->nativeformats = AST_FORMAT_SLINEAR16;
-       chan->readformat = AST_FORMAT_SLINEAR16;
-       chan->writeformat = AST_FORMAT_SLINEAR16;
-       chan->tech_pvt = ref_pvt(pvt);
+       ast_channel_stage_snapshot(chan);
+
+       ast_channel_tech_set(chan, &console_tech);
+       ast_channel_set_readformat(chan, ast_format_slin16);
+       ast_channel_set_writeformat(chan, ast_format_slin16);
+       ast_format_cap_append(caps, ast_format_slin16, 0);
+       ast_channel_nativeformats_set(chan, caps);
+       ao2_ref(caps, -1);
+       ast_channel_tech_pvt_set(chan, ref_pvt(pvt));
 
        pvt->owner = chan;
 
        if (!ast_strlen_zero(pvt->language))
-               ast_string_field_set(chan, language, pvt->language);
+               ast_channel_language_set(chan, pvt->language);
 
        ast_jb_configure(chan, &global_jbconf);
 
+       ast_channel_stage_snapshot_done(chan);
+       ast_channel_unlock(chan);
+
        if (state != AST_STATE_DOWN) {
                if (ast_pbx_start(chan)) {
-                       chan->hangupcause = AST_CAUSE_SWITCH_CONGESTION;
+                       ast_channel_hangupcause_set(chan, AST_CAUSE_SWITCH_CONGESTION);
                        ast_hangup(chan);
                        chan = NULL;
                } else
@@ -447,20 +468,20 @@ static struct ast_channel *console_new(struct console_pvt *pvt, const char *ext,
        return chan;
 }
 
-static struct ast_channel *console_request(const char *type, int format, void *data, int *cause)
+static struct ast_channel *console_request(const char *type, struct ast_format_cap *cap, const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *data, int *cause)
 {
-       int oldformat = format;
        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);
+               ast_log(LOG_ERROR, "Console device '%s' not found\n", data);
                return NULL;
        }
 
-       format &= SUPPORTED_FORMATS;
-       if (!format) {
-               ast_log(LOG_NOTICE, "Channel requested with unsupported format(s): '%d'\n", oldformat);
+       if (!(ast_format_cap_iscompatible(cap, console_tech.capabilities))) {
+               struct ast_str *cap_buf = ast_str_alloca(64);
+               ast_log(LOG_NOTICE, "Channel requested with unsupported format(s): '%s'\n",
+                       ast_format_cap_get_names(cap, &cap_buf));
                goto return_unref;
        }
 
@@ -471,7 +492,7 @@ static struct ast_channel *console_request(const char *type, int format, void *d
        }
 
        console_pvt_lock(pvt);
-       chan = console_new(pvt, NULL, NULL, AST_STATE_DOWN);
+       chan = console_new(pvt, NULL, NULL, AST_STATE_DOWN, assignedids, requestor);
        console_pvt_unlock(pvt);
 
        if (!chan)
@@ -507,7 +528,7 @@ static int console_text(struct ast_channel *c, const char *text)
 
 static int console_hangup(struct ast_channel *c)
 {
-       struct console_pvt *pvt = c->tech_pvt;
+       struct console_pvt *pvt = ast_channel_tech_pvt(c);
 
        ast_verb(1, V_BEGIN "Hangup on Console" V_END);
 
@@ -515,14 +536,14 @@ static int console_hangup(struct ast_channel *c)
        pvt->owner = NULL;
        stop_stream(pvt);
 
-       c->tech_pvt = unref_pvt(pvt);
+       ast_channel_tech_pvt_set(c, unref_pvt(pvt));
 
        return 0;
 }
 
 static int console_answer(struct ast_channel *c)
 {
-       struct console_pvt *pvt = c->tech_pvt;
+       struct console_pvt *pvt = ast_channel_tech_pvt(c);
 
        ast_verb(1, V_BEGIN "Call from Console has been Answered" V_END);
 
@@ -558,13 +579,15 @@ static struct ast_frame *console_read(struct ast_channel *chan)
        return &ast_null_frame;
 }
 
-static int console_call(struct ast_channel *c, char *dest, int timeout)
+static int console_call(struct ast_channel *c, const char *dest, int timeout)
 {
-       struct ast_frame f = { 0, };
-       struct console_pvt *pvt = c->tech_pvt;
+       struct console_pvt *pvt = ast_channel_tech_pvt(c);
+       enum ast_control_frame_type ctrl;
 
        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);
+               dest,
+               S_COR(ast_channel_caller(c)->id.name.valid, ast_channel_caller(c)->id.name.str, ""),
+               S_COR(ast_channel_caller(c)->id.number.valid, ast_channel_caller(c)->id.number.str, ""));
 
        console_pvt_lock(pvt);
 
@@ -572,25 +595,23 @@ static int console_call(struct ast_channel *c, char *dest, int timeout)
                pvt->hookstate = 1;
                console_pvt_unlock(pvt);
                ast_verb(1, V_BEGIN "Auto-answered" V_END);
-               f.frametype = AST_FRAME_CONTROL;
-               f.subclass = AST_CONTROL_ANSWER;
+               ctrl = AST_CONTROL_ANSWER;
        } else {
                console_pvt_unlock(pvt);
                ast_verb(1, V_BEGIN "Type 'console answer' to answer, or use the 'autoanswer' option "
                                "for future calls" V_END);
-               f.frametype = AST_FRAME_CONTROL;
-               f.subclass = AST_CONTROL_RINGING;
+               ctrl = AST_CONTROL_RINGING;
                ast_indicate(c, AST_CONTROL_RINGING);
        }
 
-       ast_queue_frame(c, &f);
+       ast_queue_control(c, ctrl);
 
        return start_stream(pvt);
 }
 
 static int console_write(struct ast_channel *chan, struct ast_frame *f)
 {
-       struct console_pvt *pvt = chan->tech_pvt;
+       struct console_pvt *pvt = ast_channel_tech_pvt(chan);
 
        Pa_WriteStream(pvt->stream, f->data.ptr, f->samples);
 
@@ -599,13 +620,15 @@ static int console_write(struct ast_channel *chan, struct ast_frame *f)
 
 static int console_indicate(struct ast_channel *chan, int cond, const void *data, size_t datalen)
 {
-       struct console_pvt *pvt = chan->tech_pvt;
+       struct console_pvt *pvt = ast_channel_tech_pvt(chan);
        int res = 0;
 
        switch (cond) {
        case AST_CONTROL_BUSY:
        case AST_CONTROL_CONGESTION:
        case AST_CONTROL_RINGING:
+       case AST_CONTROL_INCOMPLETE:
+       case AST_CONTROL_PVT_CAUSE_CODE:
        case -1:
                res = -1;  /* Ask for inband indications */
                break;
@@ -624,7 +647,7 @@ static int console_indicate(struct ast_channel *chan, int cond, const void *data
                break;
        default:
                ast_log(LOG_WARNING, "Don't know how to display condition %d on %s\n", 
-                       cond, chan->name);
+                       cond, ast_channel_name(chan));
                /* The core will play inband indications for us if appropriate */
                res = -1;
        }
@@ -634,7 +657,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 = newchan->tech_pvt;
+       struct console_pvt *pvt = ast_channel_tech_pvt(newchan);
 
        pvt->owner = newchan;
 
@@ -731,12 +754,11 @@ static char *cli_console_autoanswer(struct ast_cli_entry *e, int cmd,
 
        unref_pvt(pvt);
 
-       return CLI_SUCCESS;
+       return res;
 }
 
 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 = get_active_pvt();
 
        if (cmd == CLI_INIT) {
@@ -764,7 +786,7 @@ static char *cli_console_flash(struct ast_cli_entry *e, int cmd, struct ast_cli_
 
        pvt->hookstate = 0;
 
-       ast_queue_frame(pvt->owner, &f);
+       ast_queue_control(pvt->owner, AST_CONTROL_FLASH);
 
        unref_pvt(pvt);
 
@@ -796,7 +818,8 @@ static char *cli_console_dial(struct ast_cli_entry *e, int cmd, struct ast_cli_a
 
        if (pvt->owner) {       /* already in a call */
                int i;
-               struct ast_frame f = { AST_FRAME_DTMF, 0 };
+               struct ast_frame f = { AST_FRAME_DTMF };
+               const char *s;
 
                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");
@@ -806,7 +829,7 @@ static char *cli_console_dial(struct ast_cli_entry *e, int cmd, struct ast_cli_a
                s = a->argv[e->args];
                /* send the string one char at a time */
                for (i = 0; i < strlen(s); i++) {
-                       f.subclass = s[i];
+                       f.subclass.integer = s[i];
                        ast_queue_frame(pvt->owner, &f);
                }
                unref_pvt(pvt);
@@ -832,13 +855,12 @@ static char *cli_console_dial(struct ast_cli_entry *e, int cmd, struct ast_cli_a
        if (ast_exists_extension(NULL, myc, mye, 1, NULL)) {
                console_pvt_lock(pvt);
                pvt->hookstate = 1;
-               console_new(pvt, mye, myc, AST_STATE_RINGING);
+               console_new(pvt, mye, myc, AST_STATE_RINGING, NULL, NULL);
                console_pvt_unlock(pvt);
        } else
                ast_cli(a->fd, "No such extension '%s' in context '%s'\n", mye, myc);
 
-       if (s)
-               free(s);
+       ast_free(s);
 
        unref_pvt(pvt);
 
@@ -883,7 +905,7 @@ static char *cli_console_hangup(struct ast_cli_entry *e, int cmd, struct ast_cli
 
 static char *cli_console_mute(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
-       char *s;
+       const char *s;
        struct console_pvt *pvt = get_active_pvt();
        char *res = CLI_SUCCESS;
 
@@ -1020,6 +1042,7 @@ static char *cli_list_devices(struct ast_cli_entry *e, int cmd, struct ast_cli_a
                console_pvt_unlock(pvt);
                unref_pvt(pvt);
        }
+       ao2_iterator_destroy(&i);
 
        ast_cli(a->fd, "=============================================================\n\n");
 
@@ -1030,7 +1053,6 @@ static char *cli_list_devices(struct ast_cli_entry *e, int cmd, struct ast_cli_a
  */
 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 = get_active_pvt();
 
        switch (cmd) {
@@ -1065,7 +1087,7 @@ static char *cli_console_answer(struct ast_cli_entry *e, int cmd, struct ast_cli
 
        ast_indicate(pvt->owner, -1);
 
-       ast_queue_frame(pvt->owner, &f);
+       ast_queue_control(pvt->owner, AST_CONTROL_ANSWER);
 
        unref_pvt(pvt);
 
@@ -1154,7 +1176,7 @@ static char *cli_console_active(struct ast_cli_entry *e, int cmd, struct ast_cli
 
        switch (cmd) {
        case CLI_INIT:
-               e->command = "console {set|show} active [<device>]";
+               e->command = "console {set|show} active";
                e->usage =
                        "Usage: console {set|show} active [<device>]\n"
                        "       Set or show the active console device for the Asterisk CLI.\n";
@@ -1169,9 +1191,12 @@ static char *cli_console_active(struct ast_cli_entry *e, int cmd, struct ast_cli
                                if (++x > a->n && !strncasecmp(pvt->name, a->word, strlen(a->word)))
                                        res = ast_strdup(pvt->name);
                                unref_pvt(pvt);
-                               if (res)
+                               if (res) {
+                                       ao2_iterator_destroy(&i);
                                        return res;
+                               }
                        }
+                       ao2_iterator_destroy(&i);
                }
                return NULL;
        }
@@ -1194,7 +1219,7 @@ static char *cli_console_active(struct ast_cli_entry *e, int cmd, struct ast_cli
                return CLI_SUCCESS;
        }
 
-       if (!(pvt = find_pvt(a->argv[e->args]))) {
+       if (!(pvt = find_pvt(a->argv[e->args - 1]))) {
                ast_cli(a->fd, "Could not find a device called '%s'.\n", a->argv[e->args]);
                return CLI_FAILURE;
        }
@@ -1374,12 +1399,13 @@ static void destroy_pvts(void)
                }
                unref_pvt(pvt);
        }
+       ao2_iterator_destroy(&i);
 }
 
 /*!
  * \brief Load the configuration
  * \param reload if this was called due to a reload
- * \retval 0 succcess
+ * \retval 0 success
  * \retval -1 failure
  */
 static int load_config(int reload)
@@ -1426,7 +1452,7 @@ static int pvt_hash_cb(const void *obj, const int flags)
 {
        const struct console_pvt *pvt = obj;
 
-       return ast_str_hash(pvt->name);
+       return ast_str_case_hash(pvt->name);
 }
 
 static int pvt_cmp_cb(void *obj, void *arg, int flags)
@@ -1447,10 +1473,13 @@ static void stop_streams(void)
                        stop_stream(pvt);
                unref_pvt(pvt);
        }
+       ao2_iterator_destroy(&i);
 }
 
 static int unload_module(void)
 {
+       ao2_ref(console_tech.capabilities, -1);
+       console_tech.capabilities = NULL;
        ast_channel_unregister(&console_tech);
        ast_cli_unregister_multiple(cli_console, ARRAY_LEN(cli_console));
 
@@ -1466,10 +1495,25 @@ static int unload_module(void)
        return 0;
 }
 
+/*!
+ * \brief Load the module
+ *
+ * Module loading including tests for configuration or dependencies.
+ * This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE,
+ * or AST_MODULE_LOAD_SUCCESS. If a dependency or environment variable fails
+ * tests return AST_MODULE_LOAD_FAILURE. If the module can not load the 
+ * configuration file or other non-critical problem return 
+ * AST_MODULE_LOAD_DECLINE. On success return AST_MODULE_LOAD_SUCCESS.
+ */
 static int load_module(void)
 {
        PaError res;
 
+       if (!(console_tech.capabilities = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT))) {
+               return AST_MODULE_LOAD_DECLINE;
+       }
+       ast_format_cap_append(console_tech.capabilities, ast_format_slin16, 0);
+
        init_pvt(&globals, NULL);
 
        if (!(pvts = ao2_container_alloc(NUM_PVT_BUCKETS, pvt_hash_cb, pvt_cmp_cb)))
@@ -1504,6 +1548,9 @@ return_error_pa_init:
 return_error:
        if (pvts)
                ao2_ref(pvts, -1);
+       pvts = NULL;
+       ao2_ref(console_tech.capabilities, -1);
+       console_tech.capabilities = NULL;
        pvt_destructor(&globals);
 
        return AST_MODULE_LOAD_DECLINE;
@@ -1514,8 +1561,10 @@ static int reload(void)
        return load_config(1);
 }
 
-AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Console Channel Driver",
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Console Channel Driver",
+               .support_level = AST_MODULE_SUPPORT_EXTENDED,
                .load = load_module,
                .unload = unload_module,
                .reload = reload,
+               .load_pri = AST_MODPRI_CHANNEL_DRIVER,
 );