summaryrefslogtreecommitdiff
path: root/main
diff options
context:
space:
mode:
Diffstat (limited to 'main')
-rw-r--r--main/asterisk.c6
-rw-r--r--main/cdr.c62
-rw-r--r--main/cel.c652
-rw-r--r--main/channel.c333
-rw-r--r--main/cli.c13
-rw-r--r--main/devicestate.c2
-rw-r--r--main/dial.c2
-rw-r--r--main/event.c64
-rw-r--r--main/features.c70
-rw-r--r--main/loader.c1
-rw-r--r--main/logger.c2
-rw-r--r--main/manager.c2
-rw-r--r--main/pbx.c44
13 files changed, 1168 insertions, 85 deletions
diff --git a/main/asterisk.c b/main/asterisk.c
index e83534787..b497a64a5 100644
--- a/main/asterisk.c
+++ b/main/asterisk.c
@@ -118,6 +118,7 @@ int daemon(int, int); /* defined in libresolv of all places */
#include "asterisk/term.h"
#include "asterisk/manager.h"
#include "asterisk/cdr.h"
+#include "asterisk/cel.h"
#include "asterisk/pbx.h"
#include "asterisk/enum.h"
#include "asterisk/http.h"
@@ -3599,6 +3600,11 @@ int main(int argc, char *argv[])
exit(1);
}
+ if (ast_cel_engine_init()) {
+ printf("%s", term_quit());
+ exit(1);
+ }
+
if (ast_device_state_engine_init()) {
printf("%s", term_quit());
exit(1);
diff --git a/main/cdr.c b/main/cdr.c
index 610c2ab31..0658ead75 100644
--- a/main/cdr.c
+++ b/main/cdr.c
@@ -271,8 +271,12 @@ void ast_cdr_getvar(struct ast_cdr *cdr, const char *name, char **ret, char *wor
}
} else if (!strcasecmp(name, "accountcode"))
ast_copy_string(workspace, cdr->accountcode, workspacelen);
+ else if (!strcasecmp(name, "peeraccount"))
+ ast_copy_string(workspace, cdr->peeraccount, workspacelen);
else if (!strcasecmp(name, "uniqueid"))
ast_copy_string(workspace, cdr->uniqueid, workspacelen);
+ else if (!strcasecmp(name, "linkedid"))
+ ast_copy_string(workspace, cdr->linkedid, workspacelen);
else if (!strcasecmp(name, "userfield"))
ast_copy_string(workspace, cdr->userfield, workspacelen);
else if ((varbuf = ast_cdr_getvar_internal(cdr, name, recur)))
@@ -287,7 +291,7 @@ void ast_cdr_getvar(struct ast_cdr *cdr, const char *name, char **ret, char *wor
/* readonly cdr variables */
static const char * const cdr_readonly_vars[] = { "clid", "src", "dst", "dcontext", "channel", "dstchannel",
"lastapp", "lastdata", "start", "answer", "end", "duration",
- "billsec", "disposition", "amaflags", "accountcode", "uniqueid",
+ "billsec", "disposition", "amaflags", "accountcode", "uniqueid", "linkedid",
"userfield", NULL };
/*! Set a CDR channel variable
\note You can't set the CDR variables that belong to the actual CDR record, like "billsec".
@@ -298,9 +302,6 @@ int ast_cdr_setvar(struct ast_cdr *cdr, const char *name, const char *value, int
struct varshead *headp;
int x;
- if (!cdr) /* don't die if the cdr is null */
- return -1;
-
for (x = 0; cdr_readonly_vars[x]; x++) {
if (!strcasecmp(name, cdr_readonly_vars[x])) {
ast_log(LOG_ERROR, "Attempt to set the '%s' read-only variable!.\n", name);
@@ -644,6 +645,9 @@ void ast_cdr_merge(struct ast_cdr *to, struct ast_cdr *from)
if (ast_test_flag(from, AST_CDR_FLAG_LOCKED) || (ast_strlen_zero(to->accountcode) && !ast_strlen_zero(from->accountcode))) {
ast_copy_string(to->accountcode, from->accountcode, sizeof(to->accountcode));
}
+ if (ast_test_flag(from, AST_CDR_FLAG_LOCKED) || (ast_strlen_zero(to->peeraccount) && !ast_strlen_zero(from->peeraccount))) {
+ ast_copy_string(to->peeraccount, from->peeraccount, sizeof(to->peeraccount));
+ }
if (ast_test_flag(from, AST_CDR_FLAG_LOCKED) || (ast_strlen_zero(to->userfield) && !ast_strlen_zero(from->userfield))) {
ast_copy_string(to->userfield, from->userfield, sizeof(to->userfield));
}
@@ -856,11 +860,14 @@ int ast_cdr_init(struct ast_cdr *cdr, struct ast_channel *c)
cdr->disposition = (c->_state == AST_STATE_UP) ? AST_CDR_ANSWERED : AST_CDR_NOANSWER;
cdr->amaflags = c->amaflags ? c->amaflags : ast_default_amaflags;
ast_copy_string(cdr->accountcode, c->accountcode, sizeof(cdr->accountcode));
+ ast_copy_string(cdr->peeraccount, c->peeraccount, sizeof(cdr->peeraccount));
/* Destination information */
ast_copy_string(cdr->dst, S_OR(c->macroexten,c->exten), sizeof(cdr->dst));
ast_copy_string(cdr->dcontext, S_OR(c->macrocontext,c->context), sizeof(cdr->dcontext));
/* Unique call identifier */
ast_copy_string(cdr->uniqueid, c->uniqueid, sizeof(cdr->uniqueid));
+ /* Linked call identifier */
+ ast_copy_string(cdr->linkedid, c->linkedid, sizeof(cdr->linkedid));
}
}
return 0;
@@ -938,9 +945,11 @@ char *ast_cdr_flags2str(int flag)
int ast_cdr_setaccount(struct ast_channel *chan, const char *account)
{
struct ast_cdr *cdr = chan->cdr;
- char buf[BUFSIZ/2] = "";
- if (!ast_strlen_zero(chan->accountcode))
- ast_copy_string(buf, chan->accountcode, sizeof(buf));
+ const char *old_acct = "";
+
+ if (!ast_strlen_zero(chan->accountcode)) {
+ old_acct = ast_strdupa(chan->accountcode);
+ }
ast_string_field_set(chan, accountcode, account);
for ( ; cdr ; cdr = cdr->next) {
@@ -949,8 +958,39 @@ int ast_cdr_setaccount(struct ast_channel *chan, const char *account)
}
}
- /* Signal change of account code to manager */
- manager_event(EVENT_FLAG_CALL, "NewAccountCode", "Channel: %s\r\nUniqueid: %s\r\nAccountCode: %s\r\nOldAccountCode: %s\r\n", chan->name, chan->uniqueid, chan->accountcode, buf);
+ manager_event(EVENT_FLAG_CALL, "NewAccountCode",
+ "Channel: %s\r\n"
+ "Uniqueid: %s\r\n"
+ "AccountCode: %s\r\n"
+ "OldAccountCode: %s\r\n",
+ chan->name, chan->uniqueid, chan->accountcode, old_acct);
+
+ return 0;
+}
+
+int ast_cdr_setpeeraccount(struct ast_channel *chan, const char *account)
+{
+ struct ast_cdr *cdr = chan->cdr;
+ const char *old_acct = "";
+
+ if (!ast_strlen_zero(chan->peeraccount)) {
+ old_acct = ast_strdupa(chan->peeraccount);
+ }
+
+ ast_string_field_set(chan, peeraccount, account);
+ for ( ; cdr ; cdr = cdr->next) {
+ if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
+ ast_copy_string(cdr->peeraccount, chan->peeraccount, sizeof(cdr->peeraccount));
+ }
+ }
+
+ manager_event(EVENT_FLAG_CALL, "NewPeerAccount",
+ "Channel: %s\r\n"
+ "Uniqueid: %s\r\n"
+ "PeerAccount: %s\r\n"
+ "OldPeerAccount: %s\r\n",
+ chan->name, chan->uniqueid, chan->peeraccount, old_acct);
+
return 0;
}
@@ -1005,7 +1045,9 @@ int ast_cdr_update(struct ast_channel *c)
/* Copy account code et-al */
ast_copy_string(cdr->accountcode, c->accountcode, sizeof(cdr->accountcode));
-
+ ast_copy_string(cdr->peeraccount, c->peeraccount, sizeof(cdr->peeraccount));
+ ast_copy_string(cdr->linkedid, c->linkedid, sizeof(cdr->linkedid));
+
/* Destination information */ /* XXX privilege macro* ? */
ast_copy_string(cdr->dst, S_OR(c->macroexten, c->exten), sizeof(cdr->dst));
ast_copy_string(cdr->dcontext, S_OR(c->macrocontext, c->context), sizeof(cdr->dcontext));
diff --git a/main/cel.c b/main/cel.c
new file mode 100644
index 000000000..26b0fd9fa
--- /dev/null
+++ b/main/cel.c
@@ -0,0 +1,652 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2007 - 2009, Digium, Inc.
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*!
+ * \file
+ *
+ * \brief Channel Event Logging API
+ *
+ * \author Steve Murphy <murf@digium.com>
+ * \author Russell Bryant <russell@digium.com>
+ *
+ * \todo Do thorough testing of all transfer methods to ensure that BLINDTRANSFER,
+ * ATTENDEDTRANSFER, BRIDGE_START, and BRIDGE_END events are all reported
+ * as expected.
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/_private.h"
+
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/cel.h"
+#include "asterisk/logger.h"
+#include "asterisk/linkedlists.h"
+#include "asterisk/utils.h"
+#include "asterisk/config.h"
+#include "asterisk/cli.h"
+#include "asterisk/astobj2.h"
+
+/*! Is the CEL subsystem enabled ? */
+static unsigned char cel_enabled;
+
+/*! \brief CEL is off by default */
+static const unsigned char CEL_ENALBED_DEFAULT = 0;
+
+/*!
+ * \brief which events we want to track
+ *
+ * \note bit field, up to 64 events
+ */
+static int64_t eventset;
+
+/*!
+ * \brief Maximum possible CEL event IDs
+ * \note This limit is currently imposed by the eventset definition
+ */
+#define CEL_MAX_EVENT_IDS 64
+
+/*!
+ * \brief Track no events by default.
+ */
+static const int64_t CEL_DEFAULT_EVENTS = 0;
+
+/*!
+ * \brief Number of buckets for the appset container
+ */
+static const int NUM_APP_BUCKETS = 97;
+
+/*!
+ * \brief Container of Asterisk application names
+ *
+ * The apps in this container are the applications that were specified
+ * in the configuration as applications that CEL events should be generated
+ * for when they start and end on a channel.
+ */
+static struct ao2_container *appset;
+
+/*!
+ * \brief Configured date format for event timestamps
+ */
+static char cel_dateformat[256];
+
+/*!
+ * \brief Map of ast_cel_event_type to strings
+ */
+static const char const *cel_event_types[CEL_MAX_EVENT_IDS] = {
+ [0] = "ALL",
+ [AST_CEL_CHANNEL_START] = "CHAN_START",
+ [AST_CEL_CHANNEL_END] = "CHAN_END",
+ [AST_CEL_ANSWER] = "ANSWER",
+ [AST_CEL_HANGUP] = "HANGUP",
+ [AST_CEL_APP_START] = "APP_START",
+ [AST_CEL_APP_END] = "APP_END",
+ [AST_CEL_BRIDGE_START] = "BRIDGE_START",
+ [AST_CEL_BRIDGE_END] = "BRIDGE_END",
+ [AST_CEL_BRIDGE_UPDATE] = "BRIDGE_UPDATE",
+ [AST_CEL_CONF_START] = "CONF_START",
+ [AST_CEL_CONF_END] = "CONF_END",
+ [AST_CEL_PARK_START] = "PARK_START",
+ [AST_CEL_PARK_END] = "PARK_END",
+ [AST_CEL_TRANSFER] = "TRANSFER",
+ [AST_CEL_USER_DEFINED] = "USER_DEFINED",
+ [AST_CEL_CONF_ENTER] = "CONF_ENTER",
+ [AST_CEL_CONF_EXIT] = "CONF_EXIT",
+ [AST_CEL_BLINDTRANSFER] = "BLINDTRANSFER",
+ [AST_CEL_ATTENDEDTRANSFER] = "ATTENDEDTRANSFER",
+ [AST_CEL_PICKUP] = "PICKUP",
+ [AST_CEL_FORWARD] = "FORWARD",
+ [AST_CEL_3WAY_START] = "3WAY_START",
+ [AST_CEL_3WAY_END] = "3WAY_END",
+ [AST_CEL_HOOKFLASH] = "HOOKFLASH",
+ [AST_CEL_LINKEDID_END] = "LINKEDID_END",
+};
+
+/*!
+ * \brief Map of ast_cel_ama_flags to strings
+ */
+static const char const *cel_ama_flags[AST_CEL_AMA_FLAG_TOTAL] = {
+ [AST_CEL_AMA_FLAG_OMIT] = "OMIT",
+ [AST_CEL_AMA_FLAG_BILLING] = "BILLING",
+ [AST_CEL_AMA_FLAG_DOCUMENTATION] = "DOCUMENTATION",
+};
+
+unsigned int ast_cel_check_enabled(void)
+{
+ return cel_enabled;
+}
+
+static int print_app(void *obj, void *arg, int flags)
+{
+ struct ast_cli_args *a = arg;
+
+ ast_cli(a->fd, "CEL Tracking Application: %s\n", (const char *) obj);
+
+ return 0;
+}
+
+static void print_cel_sub(const struct ast_event *event, void *data)
+{
+ struct ast_cli_args *a = data;
+
+ ast_cli(a->fd, "CEL Event Subscriber: %s\n",
+ ast_event_get_ie_str(event, AST_EVENT_IE_DESCRIPTION));
+}
+
+static char *handle_cli_status(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ unsigned int i;
+ struct ast_event_sub *sub;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "cel show status";
+ e->usage =
+ "Usage: cel show status\n"
+ " Displays the Channel Event Logging system status.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ case CLI_HANDLER:
+ break;
+ }
+
+ if (a->argc > 3) {
+ return CLI_SHOWUSAGE;
+ }
+
+ ast_cli(a->fd, "CEL Logging: %s\n", cel_enabled ? "Enabled" : "Disabled");
+
+ if (!cel_enabled) {
+ return CLI_SUCCESS;
+ }
+
+ for (i = 0; i < (sizeof(eventset) * 8); i++) {
+ const char *name;
+
+ if (!(eventset & ((int64_t) 1 << i))) {
+ continue;
+ }
+
+ name = ast_cel_get_type_name(i);
+ if (strcasecmp(name, "Unknown")) {
+ ast_cli(a->fd, "CEL Tracking Event: %s\n", name);
+ }
+ }
+
+ ao2_callback(appset, OBJ_NODATA, print_app, a);
+
+ if (!(sub = ast_event_subscribe_new(AST_EVENT_SUB, print_cel_sub, a))) {
+ return CLI_FAILURE;
+ }
+ ast_event_sub_append_ie_uint(sub, AST_EVENT_IE_EVENTTYPE, AST_EVENT_CEL);
+ ast_event_report_subs(sub);
+ ast_event_sub_destroy(sub);
+ sub = NULL;
+
+ return CLI_SUCCESS;
+}
+
+static struct ast_cli_entry cli_status = AST_CLI_DEFINE(handle_cli_status, "Display the CEL status");
+
+enum ast_cel_event_type ast_cel_str_to_event_type(const char *name)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_LEN(cel_event_types); i++) {
+ if (!cel_event_types[i]) {
+ continue;
+ }
+
+ if (!strcasecmp(name, cel_event_types[i])) {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+static int ast_cel_track_event(enum ast_cel_event_type et)
+{
+ return (eventset & ((int64_t) 1 << et));
+}
+
+static void parse_events(const char *val)
+{
+ char *events = ast_strdupa(val);
+ char *cur_event;
+
+ while ((cur_event = strsep(&events, ","))) {
+ enum ast_cel_event_type event_type;
+
+ cur_event = ast_strip(cur_event);
+ if (ast_strlen_zero(cur_event)) {
+ continue;
+ }
+
+ event_type = ast_cel_str_to_event_type(cur_event);
+
+ if (event_type == 0) {
+ /* All events */
+ eventset = (int64_t) -1;
+ } else if (event_type == -1) {
+ ast_log(LOG_WARNING, "Unknown event name '%s'\n",
+ cur_event);
+ } else {
+ eventset |= ((int64_t) 1 << event_type);
+ }
+ }
+}
+
+static void parse_apps(const char *val)
+{
+ char *apps = ast_strdupa(val);
+ char *cur_app;
+
+ if (!ast_cel_track_event(AST_CEL_APP_START) && !ast_cel_track_event(AST_CEL_APP_END)) {
+ ast_log(LOG_WARNING, "An apps= config line, but not tracking APP events\n");
+ return;
+ }
+
+ while ((cur_app = strsep(&apps, ","))) {
+ char *app;
+
+ cur_app = ast_strip(cur_app);
+ if (ast_strlen_zero(cur_app)) {
+ continue;
+ }
+
+ if (!(app = ao2_alloc(strlen(cur_app) + 1, NULL))) {
+ continue;
+ }
+ strcpy(app, cur_app);
+
+ ao2_link(appset, app);
+ ao2_ref(app, -1);
+ app = NULL;
+ }
+}
+
+AST_MUTEX_DEFINE_STATIC(reload_lock);
+
+static int do_reload(void)
+{
+ struct ast_config *config;
+ const char *enabled_value;
+ const char *val;
+ int res = 0;
+ struct ast_flags config_flags = { 0, };
+ const char *s;
+
+ ast_mutex_lock(&reload_lock);
+
+ /* Reset all settings before reloading configuration */
+ cel_enabled = CEL_ENALBED_DEFAULT;
+ eventset = CEL_DEFAULT_EVENTS;
+ *cel_dateformat = '\0';
+ ao2_callback(appset, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL);
+
+ config = ast_config_load2("cel.conf", "cel", config_flags);
+
+ if (config == CONFIG_STATUS_FILEMISSING) {
+ config = NULL;
+ goto return_cleanup;
+ }
+
+ if ((enabled_value = ast_variable_retrieve(config, "general", "enable"))) {
+ cel_enabled = ast_true(enabled_value);
+ }
+
+ if (!cel_enabled) {
+ goto return_cleanup;
+ }
+
+ /* get the date format for logging */
+ if ((s = ast_variable_retrieve(config, "general", "dateformat"))) {
+ ast_copy_string(cel_dateformat, s, sizeof(cel_dateformat));
+ }
+
+ if ((val = ast_variable_retrieve(config, "general", "events"))) {
+ parse_events(val);
+ }
+
+ if ((val = ast_variable_retrieve(config, "general", "apps"))) {
+ parse_apps(val);
+ }
+
+return_cleanup:
+ ast_verb(3, "CEL logging %sabled.\n", cel_enabled ? "en" : "dis");
+
+ ast_mutex_unlock(&reload_lock);
+
+ if (config) {
+ ast_config_destroy(config);
+ }
+
+ return res;
+}
+
+const char *ast_cel_get_type_name(enum ast_cel_event_type type)
+{
+ return S_OR(cel_event_types[type], "Unknown");
+}
+
+const char *ast_cel_get_ama_flag_name(enum ast_cel_ama_flag flag)
+{
+ return S_OR(cel_ama_flags[flag], "Unknown");
+}
+
+/* called whenever a channel is destroyed or a linkedid is changed to
+ * potentially emit a CEL_LINKEDID_END event */
+
+struct channel_find_data {
+ const struct ast_channel *chan;
+ const char *linkedid;
+};
+
+static int linkedid_match(void *obj, void *arg, void *data, int flags)
+{
+ struct ast_channel *c = obj;
+ struct channel_find_data *find_dat = data;
+ int res;
+
+ ast_channel_lock(c);
+ res = (c != find_dat->chan && c->linkedid && !strcmp(find_dat->linkedid, c->linkedid));
+ ast_channel_unlock(c);
+
+ return res ? CMP_MATCH | CMP_STOP : 0;
+}
+
+void ast_cel_check_retire_linkedid(struct ast_channel *chan)
+{
+ const char *linkedid = chan->linkedid;
+ struct channel_find_data find_dat;
+
+ /* make sure we need to do all this work */
+
+ if (!ast_strlen_zero(linkedid) && ast_cel_track_event(AST_CEL_LINKEDID_END)) {
+ struct ast_channel *tmp = NULL;
+ find_dat.chan = chan;
+ find_dat.linkedid = linkedid;
+ if ((tmp = ast_channel_callback(linkedid_match, NULL, &find_dat, 0))) {
+ tmp = ast_channel_unref(tmp);
+ } else {
+ ast_cel_report_event(chan, AST_CEL_LINKEDID_END, NULL, NULL, NULL);
+ }
+ }
+}
+
+struct ast_channel *ast_cel_fabricate_channel_from_event(const struct ast_event *event)
+{
+ struct varshead *headp;
+ struct ast_var_t *newvariable;
+ char timebuf[30];
+ struct ast_channel *tchan;
+ struct ast_cel_event_record record = {
+ .version = AST_CEL_EVENT_RECORD_VERSION,
+ };
+
+ /* do not call ast_channel_alloc because this is not really a real channel */
+ if (!(tchan = ast_dummy_channel_alloc())) {
+ return NULL;
+ }
+
+ headp = &tchan->varshead;
+
+ /* first, get the variables from the event */
+ if (ast_cel_fill_record(event, &record)) {
+ ast_channel_release(tchan);
+ return NULL;
+ }
+
+ /* next, fill the channel with their data */
+ if ((newvariable = ast_var_assign("eventtype", record.event_name))) {
+ AST_LIST_INSERT_HEAD(headp, newvariable, entries);
+ }
+
+ if (ast_strlen_zero(cel_dateformat)) {
+ snprintf(timebuf, sizeof(timebuf), "%ld.%06ld", record.event_time.tv_sec, record.event_time.tv_usec);
+ } else {
+ struct ast_tm tm;
+ ast_localtime(&record.event_time, &tm, NULL);
+ ast_strftime(timebuf, sizeof(timebuf), cel_dateformat, &tm);
+ }
+
+ if ((newvariable = ast_var_assign("eventtime", timebuf))) {
+ AST_LIST_INSERT_HEAD(headp, newvariable, entries);
+ }
+
+ if ((newvariable = ast_var_assign("eventextra", record.extra))) {
+ AST_LIST_INSERT_HEAD(headp, newvariable, entries);
+ }
+
+ tchan->cid.cid_name = ast_strdup(record.caller_id_name);
+ tchan->cid.cid_num = ast_strdup(record.caller_id_num);
+ tchan->cid.cid_ani = ast_strdup(record.caller_id_ani);
+ tchan->cid.cid_rdnis = ast_strdup(record.caller_id_rdnis);
+ tchan->cid.cid_dnid = ast_strdup(record.caller_id_dnid);
+
+ ast_copy_string(tchan->exten, record.extension, sizeof(tchan->exten));
+ ast_copy_string(tchan->context, record.context, sizeof(tchan->context));
+ ast_string_field_set(tchan, name, record.channel_name);
+ ast_string_field_set(tchan, uniqueid, record.unique_id);
+ ast_string_field_set(tchan, linkedid, record.linked_id);
+ ast_string_field_set(tchan, accountcode, record.account_code);
+ ast_string_field_set(tchan, peeraccount, record.peer_account);
+ ast_string_field_set(tchan, userfield, record.user_field);
+
+ pbx_builtin_setvar_helper(tchan, "BRIDGEPEER", record.peer);
+
+ tchan->appl = ast_strdup(record.application_name);
+ tchan->data = ast_strdup(record.application_data);
+ tchan->amaflags = record.amaflag;
+
+ return tchan;
+}
+
+int ast_cel_report_event(struct ast_channel *chan, enum ast_cel_event_type event_type,
+ const char *userdefevname, const char *extra, struct ast_channel *peer2)
+{
+ struct timeval eventtime;
+ struct ast_event *ev;
+ const char *peername = "";
+ struct ast_channel *peer;
+
+ ast_channel_lock(chan);
+ peer = ast_bridged_channel(chan);
+ if (peer) {
+ ast_channel_ref(peer);
+ }
+ ast_channel_unlock(chan);
+
+ /* Make sure a reload is not occurring while we're checking to see if this
+ * is an event that we care about. We could lose an important event in this
+ * process otherwise. */
+ ast_mutex_lock(&reload_lock);
+
+ if (!cel_enabled || !ast_cel_track_event(event_type)) {
+ ast_mutex_unlock(&reload_lock);
+ return 0;
+ }
+
+ if (event_type == AST_CEL_APP_START || event_type == AST_CEL_APP_END) {
+ char *app;
+ if (!(app = ao2_find(appset, (char *) chan->appl, OBJ_POINTER))) {
+ ast_mutex_unlock(&reload_lock);
+ return 0;
+ }
+ ao2_ref(app, -1);
+ }
+
+ ast_mutex_unlock(&reload_lock);
+
+ if (peer) {
+ ast_channel_lock(peer);
+ peername = ast_strdupa(peer->name);
+ ast_channel_unlock(peer);
+ } else if (peer2) {
+ ast_channel_lock(peer2);
+ peername = ast_strdupa(peer2->name);
+ ast_channel_unlock(peer2);
+ }
+
+ if (!userdefevname) {
+ userdefevname = "";
+ }
+
+ if (!extra) {
+ extra = "";
+ }
+
+ eventtime = ast_tvnow();
+
+ ast_channel_lock(chan);
+
+ ev = ast_event_new(AST_EVENT_CEL,
+ AST_EVENT_IE_CEL_EVENT_TYPE, AST_EVENT_IE_PLTYPE_UINT, event_type,
+ AST_EVENT_IE_CEL_EVENT_TIME, AST_EVENT_IE_PLTYPE_UINT, eventtime.tv_sec,
+ AST_EVENT_IE_CEL_EVENT_TIME_USEC, AST_EVENT_IE_PLTYPE_UINT, eventtime.tv_usec,
+ AST_EVENT_IE_CEL_USEREVENT_NAME, AST_EVENT_IE_PLTYPE_STR, userdefevname,
+ AST_EVENT_IE_CEL_CIDNAME, AST_EVENT_IE_PLTYPE_STR, S_OR(chan->cid.cid_name, ""),
+ AST_EVENT_IE_CEL_CIDNUM, AST_EVENT_IE_PLTYPE_STR, S_OR(chan->cid.cid_num, ""),
+ AST_EVENT_IE_CEL_CIDANI, AST_EVENT_IE_PLTYPE_STR, S_OR(chan->cid.cid_ani, ""),
+ AST_EVENT_IE_CEL_CIDRDNIS, AST_EVENT_IE_PLTYPE_STR, S_OR(chan->cid.cid_rdnis, ""),
+ AST_EVENT_IE_CEL_CIDDNID, AST_EVENT_IE_PLTYPE_STR, S_OR(chan->cid.cid_dnid, ""),
+ AST_EVENT_IE_CEL_EXTEN, AST_EVENT_IE_PLTYPE_STR, chan->exten,
+ AST_EVENT_IE_CEL_CONTEXT, AST_EVENT_IE_PLTYPE_STR, chan->context,
+ AST_EVENT_IE_CEL_CHANNAME, AST_EVENT_IE_PLTYPE_STR, chan->name,
+ AST_EVENT_IE_CEL_APPNAME, AST_EVENT_IE_PLTYPE_STR, S_OR(chan->appl, ""),
+ AST_EVENT_IE_CEL_APPDATA, AST_EVENT_IE_PLTYPE_STR, S_OR(chan->data, ""),
+ AST_EVENT_IE_CEL_AMAFLAGS, AST_EVENT_IE_PLTYPE_UINT, chan->amaflags,
+ AST_EVENT_IE_CEL_ACCTCODE, AST_EVENT_IE_PLTYPE_STR, chan->accountcode,
+ AST_EVENT_IE_CEL_PEERACCT, AST_EVENT_IE_PLTYPE_STR, chan->peeraccount,
+ AST_EVENT_IE_CEL_UNIQUEID, AST_EVENT_IE_PLTYPE_STR, chan->uniqueid,
+ AST_EVENT_IE_CEL_LINKEDID, AST_EVENT_IE_PLTYPE_STR, chan->linkedid,
+ AST_EVENT_IE_CEL_USERFIELD, AST_EVENT_IE_PLTYPE_STR, chan->userfield,
+ AST_EVENT_IE_CEL_EXTRA, AST_EVENT_IE_PLTYPE_STR, extra,
+ AST_EVENT_IE_CEL_PEER, AST_EVENT_IE_PLTYPE_STR, peername,
+ AST_EVENT_IE_END);
+
+ ast_channel_unlock(chan);
+
+ if (peer) {
+ peer = ast_channel_unref(peer);
+ }
+
+ if (ev && ast_event_queue(ev)) {
+ ast_event_destroy(ev);
+ return -1;
+ }
+
+ return 0;
+}
+
+int ast_cel_fill_record(const struct ast_event *e, struct ast_cel_event_record *r)
+{
+ if (r->version != AST_CEL_EVENT_RECORD_VERSION) {
+ ast_log(LOG_ERROR, "Module ABI mismatch for ast_cel_event_record. "
+ "Please ensure all modules were compiled for "
+ "this version of Asterisk.\n");
+ return -1;
+ }
+
+ r->event_type = ast_event_get_ie_uint(e, AST_EVENT_IE_CEL_EVENT_TYPE);
+
+ r->event_time.tv_sec = ast_event_get_ie_uint(e, AST_EVENT_IE_CEL_EVENT_TIME);
+ r->event_time.tv_usec = ast_event_get_ie_uint(e, AST_EVENT_IE_CEL_EVENT_TIME_USEC);
+
+ r->user_defined_name = "";
+
+ if (r->event_type == AST_CEL_USER_DEFINED) {
+ r->user_defined_name = ast_event_get_ie_str(e, AST_EVENT_IE_CEL_USEREVENT_NAME);
+ r->event_name = r->user_defined_name;
+ } else {
+ r->event_name = ast_cel_get_type_name(r->event_type);
+ }
+
+ r->caller_id_name = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_CIDNAME), "");
+ r->caller_id_num = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_CIDNUM), "");
+ r->caller_id_ani = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_CIDANI), "");
+ r->caller_id_rdnis = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_CIDRDNIS), "");
+ r->caller_id_dnid = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_CIDDNID), "");
+ r->extension = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_EXTEN), "");
+ r->context = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_CONTEXT), "");
+ r->channel_name = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_CHANNAME), "");
+ r->application_name = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_APPNAME), "");
+ r->application_data = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_APPDATA), "");
+ r->account_code = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_ACCTCODE), "");
+ r->peer_account = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_ACCTCODE), "");
+ r->unique_id = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_UNIQUEID), "");
+ r->linked_id = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_LINKEDID), "");
+ r->amaflag = ast_event_get_ie_uint(e, AST_EVENT_IE_CEL_AMAFLAGS);
+ r->user_field = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_USERFIELD), "");
+ r->peer = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_PEER), "");
+ r->extra = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_EXTRA), "");
+
+ return 0;
+}
+
+static int app_hash(const void *obj, const int flags)
+{
+ return ast_str_case_hash((const char *) obj);
+}
+
+static int app_cmp(void *obj, void *arg, int flags)
+{
+ const char *app1 = obj, *app2 = arg;
+
+ return !strcasecmp(app1, app2) ? CMP_MATCH | CMP_STOP : 0;
+}
+
+static void ast_cel_engine_term(void)
+{
+ if (appset) {
+ ao2_ref(appset, -1);
+ appset = NULL;
+ }
+}
+
+int ast_cel_engine_init(void)
+{
+ if (!(appset = ao2_container_alloc(NUM_APP_BUCKETS, app_hash, app_cmp))) {
+ return -1;
+ }
+
+ if (do_reload()) {
+ ao2_ref(appset, -1);
+ appset = NULL;
+ return -1;
+ }
+
+ if (ast_cli_register(&cli_status)) {
+ ao2_ref(appset, -1);
+ appset = NULL;
+ return -1;
+ }
+
+ ast_register_atexit(ast_cel_engine_term);
+
+ return 0;
+}
+
+int ast_cel_engine_reload(void)
+{
+ return do_reload();
+}
+
diff --git a/main/channel.c b/main/channel.c
index fdfe9e941..62377f897 100644
--- a/main/channel.c
+++ b/main/channel.c
@@ -45,6 +45,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/cli.h"
#include "asterisk/translate.h"
#include "asterisk/manager.h"
+#include "asterisk/cel.h"
#include "asterisk/chanvars.h"
#include "asterisk/linkedlists.h"
#include "asterisk/indications.h"
@@ -62,6 +63,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/audiohook.h"
#include "asterisk/timing.h"
#include "asterisk/autochan.h"
+#include "asterisk/stringfields.h"
#ifdef HAVE_EPOLL
#include <sys/epoll.h>
@@ -777,13 +779,14 @@ static const struct ast_channel_tech null_tech = {
};
static void ast_channel_destructor(void *obj);
+static void ast_dummy_channel_destructor(void *obj);
/*! \brief Create a new channel structure */
-static struct ast_channel * attribute_malloc __attribute__((format(printf, 12, 0)))
+static struct ast_channel * attribute_malloc __attribute__((format(printf, 13, 0)))
__ast_channel_alloc_ap(int needqueue, int state, const char *cid_num, const char *cid_name,
const char *acctcode, const char *exten, const char *context,
- const int amaflag, const char *file, int line, const char *function,
- const char *name_fmt, va_list ap1, va_list ap2)
+ const char *linkedid, const int amaflag, const char *file, int line,
+ const char *function, const char *name_fmt, va_list ap1, va_list ap2)
{
struct ast_channel *tmp;
int x;
@@ -902,7 +905,14 @@ alertpipe_failed:
ast_string_field_build(tmp, uniqueid, "%s-%li.%d", ast_config_AST_SYSTEM_NAME,
(long) time(NULL), ast_atomic_fetchadd_int(&uniqueint, 1));
}
-
+
+ if (!ast_strlen_zero(linkedid)) {
+ ast_string_field_set(tmp, linkedid, linkedid);
+ }
+ else {
+ ast_string_field_set(tmp, linkedid, tmp->uniqueid);
+ }
+
if (!ast_strlen_zero(name_fmt)) {
/* Almost every channel is calling this function, and setting the name via the ast_string_field_build() call.
* And they all use slightly different formats for their name string.
@@ -938,11 +948,13 @@ alertpipe_failed:
strcpy(tmp->exten, "s");
tmp->priority = 1;
-
+
tmp->cdr = ast_cdr_alloc();
ast_cdr_init(tmp->cdr, tmp);
ast_cdr_start(tmp->cdr);
-
+
+ ast_cel_report_event(tmp, AST_CEL_CHANNEL_START, NULL, NULL, NULL);
+
headp = &tmp->varshead;
AST_LIST_HEAD_INIT_NOLOCK(headp);
@@ -990,8 +1002,9 @@ alertpipe_failed:
struct ast_channel *__ast_channel_alloc(int needqueue, int state, const char *cid_num,
const char *cid_name, const char *acctcode,
const char *exten, const char *context,
- const int amaflag, const char *file, int line,
- const char *function, const char *name_fmt, ...)
+ const char *linkedid, const int amaflag,
+ const char *file, int line, const char *function,
+ const char *name_fmt, ...)
{
va_list ap1, ap2;
struct ast_channel *result;
@@ -999,13 +1012,45 @@ struct ast_channel *__ast_channel_alloc(int needqueue, int state, const char *ci
va_start(ap1, name_fmt);
va_start(ap2, name_fmt);
result = __ast_channel_alloc_ap(needqueue, state, cid_num, cid_name, acctcode, exten, context,
- amaflag, file, line, function, name_fmt, ap1, ap2);
+ linkedid, amaflag, file, line, function, name_fmt, ap1, ap2);
va_end(ap1);
va_end(ap2);
return result;
}
+/* only do the minimum amount of work needed here to make a channel
+ * structure that can be used to expand channel vars */
+struct ast_channel *ast_dummy_channel_alloc(void)
+{
+ struct ast_channel *tmp;
+ struct varshead *headp;
+
+#if defined(REF_DEBUG)
+ if (!(tmp = __ao2_alloc_debug(sizeof(*tmp), ast_dummy_channel_destructor, "", file, line, function, 1))) {
+ return NULL;
+ }
+#elif defined(__AST_DEBUG_MALLOC)
+ if (!(tmp = __ao2_alloc_debug(sizeof(*tmp), ast_dummy_channel_destructor, "", file, line, function, 0))) {
+ return NULL;
+ }
+#else
+ if (!(tmp = ao2_alloc(sizeof(*tmp), ast_dummy_channel_destructor))) {
+ return NULL;
+ }
+#endif
+
+ if ((ast_string_field_init(tmp, 128))) {
+ ast_channel_unref(tmp);
+ return NULL;
+ }
+
+ headp = &tmp->varshead;
+ AST_LIST_HEAD_INIT_NOLOCK(headp);
+
+ return tmp;
+}
+
static int __ast_queue_frame(struct ast_channel *chan, struct ast_frame *fin, int head, struct ast_frame *after)
{
struct ast_frame *f;
@@ -1693,6 +1738,9 @@ static void ast_channel_destructor(void *obj)
headp = &chan->varshead;
+ ast_cel_report_event(chan, AST_CEL_CHANNEL_END, NULL, NULL, NULL);
+ ast_cel_check_retire_linkedid(chan);
+
/* Get rid of each of the data stores on the channel */
while ((datastore = AST_LIST_REMOVE_HEAD(&chan->datastores, entry)))
/* Free the data store */
@@ -1782,6 +1830,30 @@ static void ast_channel_destructor(void *obj)
ast_devstate_changed_literal(AST_DEVICE_UNKNOWN, name);
}
+/*! \brief Free a dummy channel structure */
+static void ast_dummy_channel_destructor(void *obj)
+{
+ struct ast_channel *chan = obj;
+ struct ast_var_t *vardata;
+ struct varshead *headp;
+
+ headp = &chan->varshead;
+
+ free_cid(&chan->cid);
+
+ /* loop over the variables list, freeing all data and deleting list items */
+ /* no need to lock the list, as the channel is already locked */
+ while ((vardata = AST_LIST_REMOVE_HEAD(headp, entries)))
+ ast_var_delete(vardata);
+
+ if (chan->cdr) {
+ ast_cdr_discard(chan->cdr);
+ chan->cdr = NULL;
+ }
+
+ ast_string_field_free_memory(chan);
+}
+
struct ast_datastore *ast_channel_datastore_alloc(const struct ast_datastore_info *info, const char *uid)
{
return ast_datastore_alloc(info, uid);
@@ -1964,10 +2036,30 @@ static void free_translation(struct ast_channel *clonechan)
clonechan->rawreadformat = clonechan->nativeformats;
}
+void ast_set_hangupsource(struct ast_channel *chan, const char *source, int force)
+{
+ struct ast_channel *bridge;
+
+ ast_channel_lock(chan);
+ if (force || ast_strlen_zero(chan->hangupsource)) {
+ ast_string_field_set(chan, hangupsource, source);
+ }
+ bridge = ast_bridged_channel(chan);
+ ast_channel_unlock(chan);
+
+ if (bridge && (force || ast_strlen_zero(bridge->hangupsource))) {
+ ast_channel_lock(bridge);
+ ast_string_field_set(chan, hangupsource, source);
+ ast_channel_unlock(bridge);
+ }
+}
+
/*! \brief Hangup a channel */
int ast_hangup(struct ast_channel *chan)
{
int res = 0;
+ struct ast_cdr *cdr = NULL;
+ char extra_str[64]; /* used for cel logging below */
/* Don't actually hang up a channel that will masquerade as someone else, or
if someone is going to masquerade as us */
@@ -2012,12 +2104,21 @@ int ast_hangup(struct ast_channel *chan)
sched_context_destroy(chan->sched);
chan->sched = NULL;
}
-
+
if (chan->generatordata) /* Clear any tone stuff remaining */
if (chan->generator && chan->generator->release)
chan->generator->release(chan, chan->generatordata);
chan->generatordata = NULL;
chan->generator = NULL;
+
+ snprintf(extra_str, sizeof(extra_str), "%d,%s,%s", chan->hangupcause, chan->hangupsource, S_OR(pbx_builtin_getvar_helper(chan, "DIALSTATUS"), ""));
+ ast_cel_report_event(chan, AST_CEL_HANGUP, NULL, extra_str, NULL);
+
+ if (chan->cdr) { /* End the CDR if it hasn't already */
+ ast_cdr_end(chan->cdr);
+ cdr = chan->cdr;
+ chan->cdr = NULL;
+ }
if (ast_test_flag(chan, AST_FLAG_BLOCKING)) {
ast_log(LOG_WARNING, "Hard hangup called by thread %ld on %s, while fd "
"is blocked by thread %ld in procedure %s! Expect a failure\n",
@@ -2093,9 +2194,11 @@ int ast_raw_answer(struct ast_channel *chan, int cdr_answer)
if (cdr_answer) {
ast_cdr_answer(chan->cdr);
}
+ ast_cel_report_event(chan, AST_CEL_ANSWER, NULL, NULL, NULL);
ast_channel_unlock(chan);
break;
case AST_STATE_UP:
+ ast_cel_report_event(chan, AST_CEL_ANSWER, NULL, NULL, NULL);
/* Calling ast_cdr_answer when it it has previously been called
* is essentially a no-op, so it is safe.
*/
@@ -3081,6 +3184,7 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio)
/* Answer the CDR */
ast_setstate(chan, AST_STATE_UP);
/* removed a call to ast_cdr_answer(chan->cdr) from here. */
+ ast_cel_report_event(chan, AST_CEL_ANSWER, NULL, NULL, NULL);
}
}
break;
@@ -4049,7 +4153,7 @@ struct ast_channel *ast_call_forward(struct ast_channel *caller, struct ast_chan
data = tmpchan;
type = "Local";
}
- if (!(new = ast_request(type, format, data, &cause))) {
+ if (!(new = ast_request(type, format, orig, data, &cause))) {
ast_log(LOG_NOTICE, "Unable to create channel for call forward to '%s/%s' (cause = %d)\n", type, data, cause);
handle_cause(cause, outstate);
ast_hangup(orig);
@@ -4103,7 +4207,7 @@ struct ast_channel *ast_call_forward(struct ast_channel *caller, struct ast_chan
return new;
}
-struct ast_channel *__ast_request_and_dial(const char *type, int format, void *data, int timeout, int *outstate, const char *cid_num, const char *cid_name, struct outgoing_helper *oh)
+struct ast_channel *__ast_request_and_dial(const char *type, int format, const struct ast_channel *requestor, void *data, int timeout, int *outstate, const char *cid_num, const char *cid_name, struct outgoing_helper *oh)
{
int dummy_outstate;
int cause = 0;
@@ -4117,7 +4221,7 @@ struct ast_channel *__ast_request_and_dial(const char *type, int format, void *d
else
outstate = &dummy_outstate; /* make outstate always a valid pointer */
- chan = ast_request(type, format, data, &cause);
+ chan = ast_request(type, format, requestor, data, &cause);
if (!chan) {
ast_log(LOG_NOTICE, "Unable to request channel %s/%s\n", type, (char *)data);
handle_cause(cause, outstate);
@@ -4238,12 +4342,12 @@ struct ast_channel *__ast_request_and_dial(const char *type, int format, void *d
return chan;
}
-struct ast_channel *ast_request_and_dial(const char *type, int format, void *data, int timeout, int *outstate, const char *cidnum, const char *cidname)
+struct ast_channel *ast_request_and_dial(const char *type, int format, const struct ast_channel *requestor, void *data, int timeout, int *outstate, const char *cidnum, const char *cidname)
{
- return __ast_request_and_dial(type, format, data, timeout, outstate, cidnum, cidname, NULL);
+ return __ast_request_and_dial(type, format, requestor, data, timeout, outstate, cidnum, cidname, NULL);
}
-struct ast_channel *ast_request(const char *type, int format, void *data, int *cause)
+struct ast_channel *ast_request(const char *type, int format, const struct ast_channel *requestor, void *data, int *cause)
{
struct chanlist *chan;
struct ast_channel *c;
@@ -4284,10 +4388,10 @@ struct ast_channel *ast_request(const char *type, int format, void *data, int *c
AST_RWLIST_UNLOCK(&backends);
if (!chan->tech->requester)
return NULL;
-
- if (!(c = chan->tech->requester(type, capabilities | videoformat | textformat, data, cause)))
+
+ if (!(c = chan->tech->requester(type, capabilities | videoformat | textformat, requestor, data, cause)))
return NULL;
-
+
/* no need to generate a Newchannel event here; it is done in the channel_alloc call */
return c;
}
@@ -4666,6 +4770,162 @@ static void clone_variables(struct ast_channel *original, struct ast_channel *cl
}
}
+
+
+/* return the oldest of two linkedids. linkedid is derived from
+ uniqueid which is formed like this: [systemname-]ctime.seq
+
+ The systemname, and the dash are optional, followed by the epoch
+ time followed by an integer sequence. Note that this is not a
+ decimal number, since 1.2 is less than 1.11 in uniqueid land.
+
+ To compare two uniqueids, we parse out the integer values of the
+ time and the sequence numbers and compare them, with time trumping
+ sequence.
+*/
+static const char *oldest_linkedid(const char *a, const char *b)
+{
+ const char *satime, *saseq;
+ const char *sbtime, *sbseq;
+ const char *dash;
+
+ unsigned int atime, aseq, btime, bseq;
+
+ if (ast_strlen_zero(a))
+ return b;
+
+ if (ast_strlen_zero(b))
+ return a;
+
+ satime = a;
+ sbtime = b;
+
+ /* jump over the system name */
+ if ((dash = strrchr(satime, '-'))) {
+ satime = dash+1;
+ }
+ if ((dash = strrchr(sbtime, '-'))) {
+ sbtime = dash+1;
+ }
+
+ /* the sequence comes after the '.' */
+ saseq = strchr(satime, '.');
+ sbseq = strchr(sbtime, '.');
+ if (!saseq || !sbseq)
+ return NULL;
+ saseq++;
+ sbseq++;
+
+ /* convert it all to integers */
+ atime = atoi(satime); /* note that atoi is ignoring the '.' after the time string */
+ btime = atoi(sbtime); /* note that atoi is ignoring the '.' after the time string */
+ aseq = atoi(saseq);
+ bseq = atoi(sbseq);
+
+ /* and finally compare */
+ if (atime == btime) {
+ return (aseq < bseq) ? a : b;
+ }
+ else {
+ return (atime < btime) ? a : b;
+ }
+}
+
+/*! Set the channel's linkedid to the given string, and also check to
+ * see if the channel's old linkedid is now being retired */
+static void ast_channel_change_linkedid(struct ast_channel *chan, const char *linkedid)
+{
+ /* if the linkedid for this channel is being changed from something, check... */
+ if (!ast_strlen_zero(chan->linkedid) && 0 != strcmp(chan->linkedid, linkedid)) {
+ ast_cel_check_retire_linkedid(chan);
+ }
+
+ ast_string_field_set(chan, linkedid, linkedid);
+}
+
+
+/*!
+ \brief Propagate the oldest linkedid between associated channels
+
+*/
+void ast_channel_set_linkgroup(struct ast_channel *chan, struct ast_channel *peer)
+{
+ const char* linkedid=NULL;
+ struct ast_channel *bridged;
+
+ linkedid = oldest_linkedid(chan->linkedid, peer->linkedid);
+ linkedid = oldest_linkedid(linkedid, chan->uniqueid);
+ linkedid = oldest_linkedid(linkedid, peer->uniqueid);
+ if (chan->_bridge) {
+ bridged = ast_bridged_channel(chan);
+ if (bridged != peer) {
+ linkedid = oldest_linkedid(linkedid, bridged->linkedid);
+ linkedid = oldest_linkedid(linkedid, bridged->uniqueid);
+ }
+ }
+ if (peer->_bridge) {
+ bridged = ast_bridged_channel(peer);
+ if (bridged != chan) {
+ linkedid = oldest_linkedid(linkedid, bridged->linkedid);
+ linkedid = oldest_linkedid(linkedid, bridged->uniqueid);
+ }
+ }
+
+ /* just in case setting a stringfield to itself causes problems */
+ linkedid = ast_strdupa(linkedid);
+
+ ast_channel_change_linkedid(chan, linkedid);
+ ast_channel_change_linkedid(peer, linkedid);
+ if (chan->_bridge) {
+ bridged = ast_bridged_channel(chan);
+ if (bridged != peer) {
+ ast_channel_change_linkedid(bridged, linkedid);
+ }
+ }
+ if (peer->_bridge) {
+ bridged = ast_bridged_channel(peer);
+ if (bridged != chan) {
+ ast_channel_change_linkedid(bridged, linkedid);
+ }
+ }
+}
+
+/* copy accountcode and peeraccount across during a link */
+static void ast_set_owners_and_peers(struct ast_channel *chan1,
+ struct ast_channel *chan2)
+{
+ if (!ast_strlen_zero(chan1->accountcode) && ast_strlen_zero(chan2->peeraccount)) {
+ ast_log(LOG_DEBUG, "setting peeraccount to %s for %s from data on channel %s\n",
+ chan1->accountcode, chan2->name, chan1->name);
+ ast_string_field_set(chan2, peeraccount, chan1->accountcode);
+ }
+ if (!ast_strlen_zero(chan2->accountcode) && ast_strlen_zero(chan1->peeraccount)) {
+ ast_log(LOG_DEBUG, "setting peeraccount to %s for %s from data on channel %s\n",
+ chan2->accountcode, chan1->name, chan2->name);
+ ast_string_field_set(chan1, peeraccount, chan2->accountcode);
+ }
+ if (!ast_strlen_zero(chan1->peeraccount) && ast_strlen_zero(chan2->accountcode)) {
+ ast_log(LOG_DEBUG, "setting accountcode to %s for %s from data on channel %s\n",
+ chan1->peeraccount, chan2->name, chan1->name);
+ ast_string_field_set(chan2, accountcode, chan1->peeraccount);
+ }
+ if (!ast_strlen_zero(chan2->peeraccount) && ast_strlen_zero(chan1->accountcode)) {
+ ast_log(LOG_DEBUG, "setting accountcode to %s for %s from data on channel %s\n",
+ chan2->peeraccount, chan1->name, chan2->name);
+ ast_string_field_set(chan1, accountcode, chan2->peeraccount);
+ }
+ if (0 != strcmp(chan1->accountcode, chan2->peeraccount)) {
+ ast_log(LOG_DEBUG, "changing peeraccount from %s to %s on %s to match channel %s\n",
+ chan2->peeraccount, chan1->peeraccount, chan2->name, chan1->name);
+ ast_string_field_set(chan2, peeraccount, chan1->accountcode);
+ }
+ if (0 != strcmp(chan2->accountcode, chan1->peeraccount)) {
+ ast_log(LOG_DEBUG, "changing peeraccount from %s to %s on %s to match channel %s\n",
+ chan1->peeraccount, chan2->peeraccount, chan1->name, chan2->name);
+ ast_string_field_set(chan1, peeraccount, chan2->accountcode);
+ }
+}
+
/*!
* \pre chan is locked
*/
@@ -4749,7 +5009,14 @@ int ast_do_masquerade(struct ast_channel *original)
/* Mangle the name of the clone channel */
ast_change_name(clonechan, masqn);
- /* Swap the technologies */
+ /* share linked id's */
+ ast_channel_set_linkgroup(original, clonechan);
+
+ /* Notify any managers of the change, first the masq then the other */
+ manager_event(EVENT_FLAG_CALL, "Rename", "Channel: %s\r\nNewname: %s\r\nUniqueid: %s\r\n", newn, masqn, clonechan->uniqueid);
+ manager_event(EVENT_FLAG_CALL, "Rename", "Channel: %s\r\nNewname: %s\r\nUniqueid: %s\r\n", orig, newn, original->uniqueid);
+
+ /* Swap the technologies */
t = original->tech;
original->tech = clonechan->tech;
clonechan->tech = t;
@@ -4876,7 +5143,7 @@ int ast_do_masquerade(struct ast_channel *original)
/* XXX What about blocking, softhangup, blocker, and lock and blockproc? XXX */
/* Application and data remain the same */
/* Clone exception becomes real one, as with fdno */
- ast_set_flag(original, ast_test_flag(clonechan, AST_FLAG_OUTGOING | AST_FLAG_EXCEPTION));
+ ast_copy_flags(original, clonechan, AST_FLAG_EXCEPTION | AST_FLAG_OUTGOING);
original->fdno = clonechan->fdno;
/* Schedule context remains the same */
/* Stream stuff stays the same */
@@ -4916,6 +5183,14 @@ int ast_do_masquerade(struct ast_channel *original)
/* Copy the music class */
ast_string_field_set(original, musicclass, clonechan->musicclass);
+ /* copy over accuntcode and set peeraccount across the bridge */
+ ast_string_field_set(original, accountcode, S_OR(clonechan->accountcode, ""));
+ if (original->_bridge) {
+ /* XXX - should we try to lock original->_bridge here? */
+ ast_string_field_set(original->_bridge, peeraccount, S_OR(clonechan->accountcode, ""));
+ ast_cel_report_event(original, AST_CEL_BRIDGE_UPDATE, NULL, NULL, NULL);
+ }
+
ast_debug(1, "Putting channel %s in %d/%d formats\n", original->name, wformat, rformat);
/* Okay. Last thing is to let the channel driver know about all this mess, so he
@@ -5417,6 +5692,7 @@ enum ast_bridge_result ast_channel_bridge(struct ast_channel *c0, struct ast_cha
c0->_bridge = c1;
c1->_bridge = c0;
+ ast_set_owners_and_peers(c0, c1);
o0nativeformats = c0->nativeformats;
o1nativeformats = c1->nativeformats;
@@ -5555,6 +5831,8 @@ enum ast_bridge_result ast_channel_bridge(struct ast_channel *c0, struct ast_cha
"CallerID1: %s\r\n"
"CallerID2: %s\r\n",
c0->name, c1->name, c0->uniqueid, c1->uniqueid, S_OR(c0->cid.cid_num, "<unknown>"), S_OR(c1->cid.cid_num, "<unknown>"));
+ ast_cel_report_event(c0, AST_CEL_BRIDGE_END, NULL, NULL, NULL);
+
ast_debug(1, "Returning from native bridge, channels: %s, %s\n", c0->name, c1->name);
ast_clear_flag(c0, AST_FLAG_NBRIDGE);
@@ -5565,7 +5843,6 @@ enum ast_bridge_result ast_channel_bridge(struct ast_channel *c0, struct ast_cha
c0->_bridge = NULL;
c1->_bridge = NULL;
-
return res;
} else {
ast_clear_flag(c0, AST_FLAG_NBRIDGE);
@@ -5592,6 +5869,7 @@ enum ast_bridge_result ast_channel_bridge(struct ast_channel *c0, struct ast_cha
if (ast_channel_make_compatible(c0, c1)) {
ast_log(LOG_WARNING, "Can't make %s and %s compatible\n", c0->name, c1->name);
manager_bridge_event(0, 1, c0, c1);
+ /* ast_cel_report_event(c0, AST_CEL_BRIDGE_END, NULL, NULL, NULL); */
return AST_BRIDGE_FAILED;
}
o0nativeformats = c0->nativeformats;
@@ -5619,6 +5897,7 @@ enum ast_bridge_result ast_channel_bridge(struct ast_channel *c0, struct ast_cha
c0->_bridge = NULL;
c1->_bridge = NULL;
+ ast_cel_report_event(c0, AST_CEL_BRIDGE_END, NULL, NULL, NULL);
manager_event(EVENT_FLAG_CALL, "Unlink",
"Channel1: %s\r\n"
"Channel2: %s\r\n"
@@ -6747,15 +7026,17 @@ int ast_channel_connected_line_macro(struct ast_channel *autoservice_chan, struc
* newly compiled modules will call __ast_channel_alloc() via the macros in channel.h
*/
#undef ast_channel_alloc
-struct ast_channel __attribute__((format(printf, 9, 10)))
+struct ast_channel __attribute__((format(printf, 10, 11)))
*ast_channel_alloc(int needqueue, int state, const char *cid_num,
const char *cid_name, const char *acctcode,
const char *exten, const char *context,
- const int amaflag, const char *name_fmt, ...);
+ const char *linkedid, const int amaflag,
+ const char *name_fmt, ...);
struct ast_channel *ast_channel_alloc(int needqueue, int state, const char *cid_num,
const char *cid_name, const char *acctcode,
const char *exten, const char *context,
- const int amaflag, const char *name_fmt, ...)
+ const char *linkedid, const int amaflag,
+ const char *name_fmt, ...)
{
va_list ap1, ap2;
struct ast_channel *result;
@@ -6764,7 +7045,7 @@ struct ast_channel *ast_channel_alloc(int needqueue, int state, const char *cid_
va_start(ap1, name_fmt);
va_start(ap2, name_fmt);
result = __ast_channel_alloc_ap(needqueue, state, cid_num, cid_name, acctcode, exten, context,
- amaflag, __FILE__, __LINE__, __FUNCTION__, name_fmt, ap1, ap2);
+ linkedid, amaflag, __FILE__, __LINE__, __FUNCTION__, name_fmt, ap1, ap2);
va_end(ap1);
va_end(ap2);
diff --git a/main/cli.c b/main/cli.c
index a822d1eb8..bdf8c1129 100644
--- a/main/cli.c
+++ b/main/cli.c
@@ -781,9 +781,9 @@ static char *handle_chanlist(struct ast_cli_entry *e, int cmd, struct ast_cli_ar
{
#define FORMAT_STRING "%-20.20s %-20.20s %-7.7s %-30.30s\n"
#define FORMAT_STRING2 "%-20.20s %-20.20s %-7.7s %-30.30s\n"
-#define CONCISE_FORMAT_STRING "%s!%s!%s!%d!%s!%s!%s!%s!%s!%d!%s!%s!%s\n"
-#define VERBOSE_FORMAT_STRING "%-20.20s %-20.20s %-16.16s %4d %-7.7s %-12.12s %-25.25s %-15.15s %8.8s %-11.11s %-20.20s\n"
-#define VERBOSE_FORMAT_STRING2 "%-20.20s %-20.20s %-16.16s %-4.4s %-7.7s %-12.12s %-25.25s %-15.15s %8.8s %-11.11s %-20.20s\n"
+#define CONCISE_FORMAT_STRING "%s!%s!%s!%d!%s!%s!%s!%s!%s!%s!%d!%s!%s!%s\n"
+#define VERBOSE_FORMAT_STRING "%-20.20s %-20.20s %-16.16s %4d %-7.7s %-12.12s %-25.25s %-15.15s %8.8s %-11.11s %-11.11s %-20.20s\n"
+#define VERBOSE_FORMAT_STRING2 "%-20.20s %-20.20s %-16.16s %-4.4s %-7.7s %-12.12s %-25.25s %-15.15s %8.8s %-11.11s %-11.11s %-20.20s\n"
struct ast_channel *c = NULL;
int numchans = 0, concise = 0, verbose = 0, count = 0;
@@ -824,7 +824,7 @@ static char *handle_chanlist(struct ast_cli_entry *e, int cmd, struct ast_cli_ar
ast_cli(a->fd, FORMAT_STRING2, "Channel", "Location", "State", "Application(Data)");
else if (verbose)
ast_cli(a->fd, VERBOSE_FORMAT_STRING2, "Channel", "Context", "Extension", "Priority", "State", "Application", "Data",
- "CallerID", "Duration", "Accountcode", "BridgedTo");
+ "CallerID", "Duration", "Accountcode", "PeerAccount", "BridgedTo");
}
if (!count && !(iter = ast_channel_iterator_all_new(0))) {
@@ -857,6 +857,7 @@ static char *handle_chanlist(struct ast_cli_entry *e, int cmd, struct ast_cli_ar
S_OR(c->data, ""), /* XXX different from verbose ? */
S_OR(c->cid.cid_num, ""),
S_OR(c->accountcode, ""),
+ S_OR(c->peeraccount, ""),
c->amaflags,
durbuf,
bc ? bc->name : "(None)",
@@ -868,6 +869,7 @@ static char *handle_chanlist(struct ast_cli_entry *e, int cmd, struct ast_cli_ar
S_OR(c->cid.cid_num, ""),
durbuf,
S_OR(c->accountcode, ""),
+ S_OR(c->peeraccount, ""),
bc ? bc->name : "(None)");
} else {
char locbuf[40] = "(None)";
@@ -1355,6 +1357,7 @@ static char *handle_showchan(struct ast_cli_entry *e, int cmd, struct ast_cli_ar
" Name: %s\n"
" Type: %s\n"
" UniqueID: %s\n"
+ " LinkedID: %s\n"
" Caller ID: %s\n"
" Caller ID Name: %s\n"
" DNID Digits: %s\n"
@@ -1382,7 +1385,7 @@ static char *handle_showchan(struct ast_cli_entry *e, int cmd, struct ast_cli_ar
" Application: %s\n"
" Data: %s\n"
" Blocking in: %s\n",
- c->name, c->tech->type, c->uniqueid,
+ c->name, c->tech->type, c->uniqueid, c->linkedid,
S_OR(c->cid.cid_num, "(N/A)"),
S_OR(c->cid.cid_name, "(N/A)"),
S_OR(c->cid.cid_dnid, "(N/A)"),
diff --git a/main/devicestate.c b/main/devicestate.c
index 17178faf2..564aecfc9 100644
--- a/main/devicestate.c
+++ b/main/devicestate.c
@@ -818,7 +818,7 @@ int ast_enable_distributed_devstate(void)
}
devstate_collector.event_sub = ast_event_subscribe(AST_EVENT_DEVICE_STATE_CHANGE,
- devstate_change_collector_cb, NULL, AST_EVENT_IE_END);
+ devstate_change_collector_cb, "devicestate_engine_enable_distributed", NULL, AST_EVENT_IE_END);
if (!devstate_collector.event_sub) {
ast_log(LOG_ERROR, "Failed to create subscription for the device state change collector\n");
diff --git a/main/dial.c b/main/dial.c
index 1f65f50c7..4c57cb12d 100644
--- a/main/dial.c
+++ b/main/dial.c
@@ -262,7 +262,7 @@ static int begin_dial_channel(struct ast_dial_channel *channel, struct ast_chann
ast_copy_string(numsubst, channel->device, sizeof(numsubst));
/* If we fail to create our owner channel bail out */
- if (!(channel->owner = ast_request(channel->tech, chan ? chan->nativeformats : AST_FORMAT_AUDIO_MASK, numsubst, &channel->cause)))
+ if (!(channel->owner = ast_request(channel->tech, chan ? chan->nativeformats : AST_FORMAT_AUDIO_MASK, chan, numsubst, &channel->cause)))
return -1;
channel->owner->appl = "AppDial2";
diff --git a/main/event.c b/main/event.c
index 3370cf8b3..6a70a6741 100644
--- a/main/event.c
+++ b/main/event.c
@@ -119,6 +119,7 @@ struct ast_event_ie_val {
struct ast_event_sub {
enum ast_event_type type;
ast_event_cb_t cb;
+ char description[64];
void *userdata;
uint32_t uniqueid;
AST_LIST_HEAD_NOLOCK(, ast_event_ie_val) ie_vals;
@@ -195,6 +196,7 @@ static struct event_name {
{ AST_EVENT_UNSUB, "Unsubscription" },
{ AST_EVENT_DEVICE_STATE, "DeviceState" },
{ AST_EVENT_DEVICE_STATE_CHANGE, "DeviceStateChange" },
+ { AST_EVENT_CEL, "CEL" },
};
/*!
@@ -206,16 +208,38 @@ static struct ie_map {
const char *name;
} ie_maps[] = {
{ 0, 0, "" },
- { AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_UINT, "NewMessages" },
- { AST_EVENT_IE_OLDMSGS, AST_EVENT_IE_PLTYPE_UINT, "OldMessages" },
- { AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, "Mailbox" },
- { AST_EVENT_IE_UNIQUEID, AST_EVENT_IE_PLTYPE_UINT, "UniqueID" },
- { AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, "EventType" },
- { AST_EVENT_IE_EXISTS, AST_EVENT_IE_PLTYPE_UINT, "Exists" },
- { AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR, "Device" },
- { AST_EVENT_IE_STATE, AST_EVENT_IE_PLTYPE_UINT, "State" },
- { AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, "Context" },
- { AST_EVENT_IE_EID, AST_EVENT_IE_PLTYPE_RAW, "EntityID" },
+ { AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_UINT, "NewMessages" },
+ { AST_EVENT_IE_OLDMSGS, AST_EVENT_IE_PLTYPE_UINT, "OldMessages" },
+ { AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, "Mailbox" },
+ { AST_EVENT_IE_UNIQUEID, AST_EVENT_IE_PLTYPE_UINT, "UniqueID" },
+ { AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, "EventType" },
+ { AST_EVENT_IE_EXISTS, AST_EVENT_IE_PLTYPE_UINT, "Exists" },
+ { AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR, "Device" },
+ { AST_EVENT_IE_STATE, AST_EVENT_IE_PLTYPE_UINT, "State" },
+ { AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, "Context" },
+ { AST_EVENT_IE_EID, AST_EVENT_IE_PLTYPE_RAW, "EntityID" },
+ { AST_EVENT_IE_CEL_EVENT_TYPE, AST_EVENT_IE_PLTYPE_UINT, "CELEventType" },
+ { AST_EVENT_IE_CEL_EVENT_TIME, AST_EVENT_IE_PLTYPE_UINT, "CELEventTime" },
+ { AST_EVENT_IE_CEL_EVENT_TIME_USEC, AST_EVENT_IE_PLTYPE_UINT, "CELEventTimeUSec" },
+ { AST_EVENT_IE_CEL_USEREVENT_NAME, AST_EVENT_IE_PLTYPE_UINT, "CELUserEventName" },
+ { AST_EVENT_IE_CEL_CIDNAME, AST_EVENT_IE_PLTYPE_STR, "CELCIDName" },
+ { AST_EVENT_IE_CEL_CIDNUM, AST_EVENT_IE_PLTYPE_STR, "CELCIDNum" },
+ { AST_EVENT_IE_CEL_EXTEN, AST_EVENT_IE_PLTYPE_STR, "CELExten" },
+ { AST_EVENT_IE_CEL_CONTEXT, AST_EVENT_IE_PLTYPE_STR, "CELContext" },
+ { AST_EVENT_IE_CEL_CHANNAME, AST_EVENT_IE_PLTYPE_STR, "CELChanName" },
+ { AST_EVENT_IE_CEL_APPNAME, AST_EVENT_IE_PLTYPE_STR, "CELAppName" },
+ { AST_EVENT_IE_CEL_APPDATA, AST_EVENT_IE_PLTYPE_STR, "CELAppData" },
+ { AST_EVENT_IE_CEL_AMAFLAGS, AST_EVENT_IE_PLTYPE_STR, "CELAMAFlags" },
+ { AST_EVENT_IE_CEL_ACCTCODE, AST_EVENT_IE_PLTYPE_UINT, "CELAcctCode" },
+ { AST_EVENT_IE_CEL_UNIQUEID, AST_EVENT_IE_PLTYPE_STR, "CELUniqueID" },
+ { AST_EVENT_IE_CEL_USERFIELD, AST_EVENT_IE_PLTYPE_STR, "CELUserField" },
+ { AST_EVENT_IE_CEL_CIDANI, AST_EVENT_IE_PLTYPE_STR, "CELCIDani" },
+ { AST_EVENT_IE_CEL_CIDRDNIS, AST_EVENT_IE_PLTYPE_STR, "CELCIDrdnis" },
+ { AST_EVENT_IE_CEL_CIDDNID, AST_EVENT_IE_PLTYPE_STR, "CELCIDdnid" },
+ { AST_EVENT_IE_CEL_PEER, AST_EVENT_IE_PLTYPE_STR, "CELPeer" },
+ { AST_EVENT_IE_CEL_LINKEDID, AST_EVENT_IE_PLTYPE_STR, "CELLinkedID" },
+ { AST_EVENT_IE_CEL_PEERACCT, AST_EVENT_IE_PLTYPE_STR, "CELPeerAcct" },
+ { AST_EVENT_IE_CEL_EXTRA, AST_EVENT_IE_PLTYPE_STR, "CELExtra" },
};
const char *ast_event_get_type_name(const struct ast_event *event)
@@ -535,8 +559,9 @@ static struct ast_event *gen_sub_event(struct ast_event_sub *sub)
struct ast_event *event;
event = ast_event_new(AST_EVENT_SUB,
- AST_EVENT_IE_UNIQUEID, AST_EVENT_IE_PLTYPE_UINT, sub->uniqueid,
- AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, sub->type,
+ AST_EVENT_IE_UNIQUEID, AST_EVENT_IE_PLTYPE_UINT, sub->uniqueid,
+ AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, sub->type,
+ AST_EVENT_IE_DESCRIPTION, AST_EVENT_IE_PLTYPE_STR, sub->description,
AST_EVENT_IE_END);
if (!event)
@@ -773,7 +798,7 @@ int ast_event_sub_activate(struct ast_event_sub *sub)
}
struct ast_event_sub *ast_event_subscribe(enum ast_event_type type, ast_event_cb_t cb,
- void *userdata, ...)
+ char *description, void *userdata, ...)
{
va_list ap;
enum ast_event_ie_type ie_type;
@@ -783,6 +808,8 @@ struct ast_event_sub *ast_event_subscribe(enum ast_event_type type, ast_event_cb
return NULL;
}
+ ast_copy_string(sub->description, description, sizeof(sub->description));
+
va_start(ap, userdata);
for (ie_type = va_arg(ap, enum ast_event_type);
ie_type != AST_EVENT_IE_END;
@@ -843,6 +870,11 @@ void ast_event_sub_destroy(struct ast_event_sub *sub)
ast_free(sub);
}
+const char *ast_event_subscriber_get_description(struct ast_event_sub *sub)
+{
+ return sub ? sub->description : NULL;
+}
+
struct ast_event_sub *ast_event_unsubscribe(struct ast_event_sub *sub)
{
struct ast_event *event;
@@ -856,8 +888,9 @@ struct ast_event_sub *ast_event_unsubscribe(struct ast_event_sub *sub)
AST_EVENT_IE_END) != AST_EVENT_SUB_NONE) {
event = ast_event_new(AST_EVENT_UNSUB,
- AST_EVENT_IE_UNIQUEID, AST_EVENT_IE_PLTYPE_UINT, sub->uniqueid,
- AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, sub->type,
+ AST_EVENT_IE_UNIQUEID, AST_EVENT_IE_PLTYPE_UINT, sub->uniqueid,
+ AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, sub->type,
+ AST_EVENT_IE_DESCRIPTION, AST_EVENT_IE_PLTYPE_STR, sub->description,
AST_EVENT_IE_END);
if (event) {
@@ -1330,6 +1363,7 @@ int ast_event_queue(struct ast_event *event)
if (ast_event_check_subscriber(host_event_type, AST_EVENT_IE_END)
== AST_EVENT_SUB_NONE) {
ast_event_destroy(event);
+ ast_log(LOG_NOTICE, "Event destroyed, no subscriber\n");
return 0;
}
diff --git a/main/features.c b/main/features.c
index 22e3a2b65..bb716fa80 100644
--- a/main/features.c
+++ b/main/features.c
@@ -55,6 +55,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/audiohook.h"
#include "asterisk/global_datastores.h"
#include "asterisk/astobj2.h"
+#include "asterisk/cel.h"
/*** DOCUMENTATION
<application name="Bridge" language="en_US">
@@ -426,7 +427,7 @@ static void check_goto_on_transfer(struct ast_channel *chan)
goto_on_transfer = ast_strdupa(val);
- if (!(xferchan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "%s", chan->name)))
+ if (!(xferchan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", chan->linkedid, 0, "%s", chan->name)))
return;
for (x = goto_on_transfer; x && *x; x++) {
@@ -808,6 +809,8 @@ static int park_call_full(struct ast_channel *chan, struct ast_channel *peer, st
pthread_kill(parking_thread, SIGURG);
ast_verb(2, "Parked %s on %d (lot %s). Will timeout back to extension [%s] %s, %d in %d seconds\n", pu->chan->name, pu->parkingnum, pu->parkinglot->name, pu->context, pu->exten, pu->priority, (pu->parkingtime/1000));
+ ast_cel_report_event(pu->chan, AST_CEL_PARK_START, NULL, pu->parkinglot->name, peer);
+
if (peer) {
event_from = peer->name;
} else {
@@ -895,7 +898,7 @@ static int masq_park_call(struct ast_channel *rchan, struct ast_channel *peer, i
}
/* Make a new, fake channel that we'll use to masquerade in the real one */
- if (!(chan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, rchan->accountcode, rchan->exten, rchan->context, rchan->amaflags, "Parked/%s",rchan->name))) {
+ if (!(chan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, rchan->accountcode, rchan->exten, rchan->context, rchan->linkedid, rchan->amaflags, "Parked/%s",rchan->name))) {
ast_log(LOG_WARNING, "Unable to create parked channel\n");
return -1;
}
@@ -1345,6 +1348,7 @@ static int builtin_blindtransfer(struct ast_channel *chan, struct ast_channel *p
}
/*! \todo XXX Maybe we should have another message here instead of invalid extension XXX */
} else if (ast_exists_extension(transferee, transferer_real_context, xferto, 1, transferer->cid.cid_num)) {
+ ast_cel_report_event(transferer, AST_CEL_BLINDTRANSFER, NULL, xferto, transferee);
pbx_builtin_setvar_helper(transferer, "BLINDTRANSFER", transferee->name);
pbx_builtin_setvar_helper(transferee, "BLINDTRANSFER", transferer->name);
res=finishup(transferee);
@@ -1561,6 +1565,9 @@ static int builtin_atxfer(struct ast_channel *chan, struct ast_channel *peer, st
ast_party_connected_line_free(&connected_line);
return AST_FEATURE_RETURN_SUCCESS;
}
+
+ ast_cel_report_event(transferee, AST_CEL_ATTENDEDTRANSFER, NULL, NULL, newchan);
+
if (check_compat(transferee, newchan)) {
finishup(transferee);
ast_party_connected_line_free(&connected_line);
@@ -1577,7 +1584,7 @@ static int builtin_atxfer(struct ast_channel *chan, struct ast_channel *peer, st
ast_party_connected_line_free(&connected_line);
return -1;
}
- xferchan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Transfered/%s", transferee->name);
+ xferchan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", transferee->linkedid, 0, "Transfered/%s", transferee->name);
if (!xferchan) {
ast_hangup(newchan);
ast_party_connected_line_free(&connected_line);
@@ -1731,6 +1738,8 @@ static int builtin_atxfer(struct ast_channel *chan, struct ast_channel *peer, st
if (!newchan)
return -1;
+ ast_cel_report_event(transferee, AST_CEL_ATTENDEDTRANSFER, NULL, NULL, newchan);
+
/* newchan is up, we should prepare transferee and bridge them */
if (check_compat(transferee, newchan)) {
finishup(transferee);
@@ -1746,7 +1755,7 @@ static int builtin_atxfer(struct ast_channel *chan, struct ast_channel *peer, st
return -1;
}
- xferchan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Transfered/%s", transferee->name);
+ xferchan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", transferee->linkedid, 0, "Transfered/%s", transferee->name);
if (!xferchan) {
ast_hangup(newchan);
return -1;
@@ -2315,7 +2324,7 @@ static struct ast_channel *feature_request_and_dial(struct ast_channel *caller,
int x, len = 0;
char *disconnect_code = NULL, *dialed_code = NULL;
- if (!(chan = ast_request(type, format, data, &cause))) {
+ if (!(chan = ast_request(type, format, caller, data, &cause))) {
ast_log(LOG_NOTICE, "Unable to request channel %s/%s\n", type, (char *)data);
switch(cause) {
case AST_CAUSE_BUSY:
@@ -2482,6 +2491,27 @@ done:
return chan;
}
+void ast_channel_log(char *title, struct ast_channel *chan);
+
+void ast_channel_log(char *title, struct ast_channel *chan) /* for debug, this is handy enough to justify keeping it in the source */
+{
+ ast_log(LOG_NOTICE, "______ %s (%lx)______\n", title, (unsigned long)chan);
+ ast_log(LOG_NOTICE, "CHAN: name: %s; appl: %s; data: %s; contxt: %s; exten: %s; pri: %d;\n",
+ chan->name, chan->appl, chan->data, chan->context, chan->exten, chan->priority);
+ ast_log(LOG_NOTICE, "CHAN: acctcode: %s; dialcontext: %s; amaflags: %x; maccontxt: %s; macexten: %s; macpri: %d;\n",
+ chan->accountcode, chan->dialcontext, chan->amaflags, chan->macrocontext, chan->macroexten, chan->macropriority);
+ ast_log(LOG_NOTICE, "CHAN: masq: %p; masqr: %p; _bridge: %p; uniqueID: %s; linkedID:%s\n",
+ chan->masq, chan->masqr,
+ chan->_bridge, chan->uniqueid, chan->linkedid);
+ if (chan->masqr)
+ ast_log(LOG_NOTICE, "CHAN: masquerading as: %s; cdr: %p;\n",
+ chan->masqr->name, chan->masqr->cdr);
+ if (chan->_bridge)
+ ast_log(LOG_NOTICE, "CHAN: Bridged to %s\n", chan->_bridge->name);
+
+ ast_log(LOG_NOTICE, "===== done ====\n");
+}
+
/*!
* \brief return the first unlocked cdr in a possible chain
*/
@@ -2661,6 +2691,26 @@ int ast_bridge_call(struct ast_channel *chan,struct ast_channel *peer,struct ast
}
}
+#ifdef FOR_DEBUG
+ /* show the two channels and cdrs involved in the bridge for debug & devel purposes */
+ ast_channel_log("Pre-bridge CHAN Channel info", chan);
+ ast_channel_log("Pre-bridge PEER Channel info", peer);
+#endif
+ /* two channels are being marked as linked here */
+ ast_channel_set_linkgroup(chan,peer);
+
+ /* copy the userfield from the B-leg to A-leg if applicable */
+ if (chan->cdr && peer->cdr && !ast_strlen_zero(peer->cdr->userfield)) {
+ char tmp[256];
+ if (!ast_strlen_zero(chan->cdr->userfield)) {
+ snprintf(tmp, sizeof(tmp), "%s;%s", chan->cdr->userfield, peer->cdr->userfield);
+ ast_cdr_appenduserfield(chan, tmp);
+ } else
+ ast_cdr_setuserfield(chan, peer->cdr->userfield);
+ /* free the peer's cdr without ast_cdr_free complaining */
+ ast_free(peer->cdr);
+ peer->cdr = NULL;
+ }
ast_copy_string(orig_channame,chan->name,sizeof(orig_channame));
ast_copy_string(orig_peername,peer->name,sizeof(orig_peername));
orig_peer_cdr = peer_cdr;
@@ -2733,6 +2783,7 @@ int ast_bridge_call(struct ast_channel *chan,struct ast_channel *peer,struct ast
}
}
}
+ ast_cel_report_event(chan, AST_CEL_BRIDGE_START, NULL, NULL, NULL);
for (;;) {
struct ast_channel *other; /* used later */
@@ -3213,6 +3264,7 @@ int manage_parkinglot(struct ast_parkinglot *curlot, fd_set *rfds, fd_set *efds,
set_c_e_p(chan, pu->context, pu->exten, pu->priority);
}
post_manager_event("ParkedCallTimeOut", pu);
+ ast_cel_report_event(pu->chan, AST_CEL_PARK_END, NULL, "ParkedCallTimeOut", NULL);
ast_verb(2, "Timeout for %s parked on %d (%s). Returning to %s,%s,%d\n", pu->chan->name, pu->parkingnum, pu->parkinglot->name, pu->chan->context, pu->chan->exten, pu->chan->priority);
/* Start up the PBX, or hang them up */
@@ -3251,6 +3303,7 @@ int manage_parkinglot(struct ast_parkinglot *curlot, fd_set *rfds, fd_set *efds,
if (f)
ast_frfree(f);
post_manager_event("ParkedCallGiveUp", pu);
+ ast_cel_report_event(pu->chan, AST_CEL_PARK_END, NULL, "ParkedCallGiveUp", NULL);
/* There's a problem, hang them up*/
ast_verb(2, "%s got tired of being parked\n", chan->name);
@@ -3495,6 +3548,7 @@ static int park_exec_full(struct ast_channel *chan, const char *data, struct ast
} else
ast_log(LOG_WARNING, "Whoa, no parking context?\n");
+ ast_cel_report_event(pu->chan, AST_CEL_PARK_END, NULL, "UnParkedCall", chan);
manager_event(EVENT_FLAG_CALL, "UnParkedCall",
"Exten: %s\r\n"
"Channel: %s\r\n"
@@ -4294,7 +4348,7 @@ static int action_bridge(struct mansession *s, const struct message *m)
/* create the placeholder channels and grab the other channels */
if (!(tmpchana = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL,
- NULL, NULL, 0, "Bridge/%s", chana->name))) {
+ NULL, NULL, chana->linkedid, 0, "Bridge/%s", chana->name))) {
astman_send_error(s, m, "Unable to create temporary channel!");
chana = ast_channel_unref(chana);
return 1;
@@ -4321,7 +4375,7 @@ static int action_bridge(struct mansession *s, const struct message *m)
/* create the placeholder channels and grab the other channels */
if (!(tmpchanb = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL,
- NULL, NULL, 0, "Bridge/%s", chanb->name))) {
+ NULL, NULL, chanb->linkedid, 0, "Bridge/%s", chanb->name))) {
astman_send_error(s, m, "Unable to create temporary channels!");
ast_hangup(tmpchana);
chanb = ast_channel_unref(chanb);
@@ -4715,7 +4769,7 @@ static int bridge_exec(struct ast_channel *chan, const char *data)
/* try to allocate a place holder where current_dest_chan will be placed */
if (!(final_dest_chan = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL,
- NULL, NULL, 0, "Bridge/%s", current_dest_chan->name))) {
+ NULL, NULL, current_dest_chan->linkedid, 0, "Bridge/%s", current_dest_chan->name))) {
ast_log(LOG_WARNING, "Cannot create placeholder channel for chan %s\n", args.dest_chan);
manager_event(EVENT_FLAG_CALL, "BridgeExec",
"Response: Failed\r\n"
diff --git a/main/loader.c b/main/loader.c
index a8e986409..451d33194 100644
--- a/main/loader.c
+++ b/main/loader.c
@@ -258,6 +258,7 @@ static struct reload_classes {
{ "dsp", ast_dsp_reload},
{ "udptl", ast_udptl_reload },
{ "indications", ast_indications_reload },
+ { "cel", ast_cel_engine_reload },
{ NULL, NULL }
};
diff --git a/main/logger.c b/main/logger.c
index 34009053a..02cf4f4fc 100644
--- a/main/logger.c
+++ b/main/logger.c
@@ -535,7 +535,7 @@ static int rotate_file(const char *filename)
}
if (!ast_strlen_zero(exec_after_rotate)) {
- struct ast_channel *c = ast_channel_alloc(0, 0, "", "", "", "", "", 0, "Logger/rotate");
+ struct ast_channel *c = ast_dummy_channel_alloc();
char buf[512];
pbx_builtin_setvar_helper(c, "filename", filename);
pbx_substitute_variables_helper(c, exec_after_rotate, buf, sizeof(buf));
diff --git a/main/manager.c b/main/manager.c
index a2e85f351..76e24d170 100644
--- a/main/manager.c
+++ b/main/manager.c
@@ -2488,7 +2488,7 @@ static int action_getvar(struct mansession *s, const struct message *m)
if (varname[strlen(varname) - 1] == ')') {
if (!c) {
- c = ast_channel_alloc(0, 0, "", "", "", "", "", 0, "Bogus/manager");
+ c = ast_dummy_channel_alloc();
if (c) {
ast_func_read(c, (char *) varname, workspace, sizeof(workspace));
c = ast_channel_release(c);
diff --git a/main/pbx.c b/main/pbx.c
index 32838a0e6..c7de70f87 100644
--- a/main/pbx.c
+++ b/main/pbx.c
@@ -46,6 +46,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/file.h"
#include "asterisk/callerid.h"
#include "asterisk/cdr.h"
+#include "asterisk/cel.h"
#include "asterisk/config.h"
#include "asterisk/term.h"
#include "asterisk/time.h"
@@ -1356,6 +1357,8 @@ int pbx_exec(struct ast_channel *c, /*!< Channel */
c->appl = app->name;
c->data = data;
+ ast_cel_report_event(c, AST_CEL_APP_START, NULL, NULL, NULL);
+
if (app->module)
u = __ast_module_user_add(app->module, c);
if (strcasecmp(app->name, "system") && !ast_strlen_zero(data) &&
@@ -1367,6 +1370,7 @@ int pbx_exec(struct ast_channel *c, /*!< Channel */
res = app->execute(c, S_OR(data, ""));
if (app->module && u)
__ast_module_user_remove(app->module, u);
+ ast_cel_report_event(c, AST_CEL_APP_END, NULL, NULL, NULL);
/* restore channel values */
c->appl = saved_c_appl;
c->data = saved_c_data;
@@ -3622,7 +3626,7 @@ void ast_str_substitute_variables_full(struct ast_str **buf, ssize_t maxlen, str
cp4 = ast_func_read2(c, finalvars, &substr3, 0) ? NULL : ast_str_buffer(substr3);
} else {
struct varshead old;
- struct ast_channel *bogus = ast_channel_alloc(0, 0, "", "", "", "", "", 0, "Bogus/%p", vars);
+ struct ast_channel *bogus = ast_dummy_channel_alloc();
if (bogus) {
memcpy(&old, &bogus->varshead, sizeof(old));
memcpy(&bogus->varshead, headp, sizeof(bogus->varshead));
@@ -3821,14 +3825,14 @@ void pbx_substitute_variables_helper_full(struct ast_channel *c, struct varshead
cp4 = ast_func_read(c, vars, workspace, VAR_BUF_SIZE) ? NULL : workspace;
else {
struct varshead old;
- struct ast_channel *bogus = ast_channel_alloc(0, 0, "", "", "", "", "", 0, "Bogus/%p", vars);
- if (bogus) {
- memcpy(&old, &bogus->varshead, sizeof(old));
- memcpy(&bogus->varshead, headp, sizeof(bogus->varshead));
- cp4 = ast_func_read(bogus, vars, workspace, VAR_BUF_SIZE) ? NULL : workspace;
+ struct ast_channel *c = ast_dummy_channel_alloc();
+ if (c) {
+ memcpy(&old, &c->varshead, sizeof(old));
+ memcpy(&c->varshead, headp, sizeof(c->varshead));
+ cp4 = ast_func_read(c, vars, workspace, VAR_BUF_SIZE) ? NULL : workspace;
/* Don't deallocate the varshead that was passed in */
- memcpy(&bogus->varshead, &old, sizeof(bogus->varshead));
- bogus = ast_channel_release(bogus);
+ memcpy(&c->varshead, &old, sizeof(c->varshead));
+ c = ast_channel_release(c);
} else {
ast_log(LOG_ERROR, "Unable to allocate bogus channel for variable substitution. Function results may be blank.\n");
}
@@ -7598,7 +7602,7 @@ int ast_async_goto(struct ast_channel *chan, const char *context, const char *ex
/* In order to do it when the channel doesn't really exist within
the PBX, we have to make a new channel, masquerade, and start the PBX
at the new location */
- struct ast_channel *tmpchan = ast_channel_alloc(0, chan->_state, 0, 0, chan->accountcode, chan->exten, chan->context, chan->amaflags, "AsyncGoto/%s", chan->name);
+ struct ast_channel *tmpchan = ast_channel_alloc(0, chan->_state, 0, 0, chan->accountcode, chan->exten, chan->context, chan->linkedid, chan->amaflags, "AsyncGoto/%s", chan->name);
if (!tmpchan) {
res = -1;
} else {
@@ -7905,7 +7909,10 @@ static int ast_add_extension2_lockopt(struct ast_context *con,
/* If we are adding a hint evalulate in variables and global variables */
if (priority == PRIORITY_HINT && strstr(application, "${") && !strstr(extension, "_")) {
- struct ast_channel *c = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", extension, con->name, 0, "Bogus/%s", __PRETTY_FUNCTION__);
+ struct ast_channel *c = ast_dummy_channel_alloc();
+ ast_copy_string(c->exten, extension, sizeof(c->exten));
+ ast_copy_string(c->context, con->name, sizeof(c->context));
+
pbx_substitute_variables_helper(c, application, expand_buf, sizeof(expand_buf));
application = expand_buf;
ast_channel_release(c);
@@ -8154,11 +8161,12 @@ static void *async_wait(void *data)
static int ast_pbx_outgoing_cdr_failed(void)
{
/* allocate a channel */
- struct ast_channel *chan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "%s", "");
+ struct ast_channel *chan = ast_dummy_channel_alloc();
if (!chan)
return -1; /* failure */
+ chan->cdr = ast_cdr_alloc();
if (!chan->cdr) {
/* allocation of the cdr failed */
chan = ast_channel_release(chan); /* free the channel */
@@ -8194,7 +8202,7 @@ int ast_pbx_outgoing_exten(const char *type, int format, void *data, int timeout
oh.vars = vars;
oh.parent_channel = NULL;
- chan = __ast_request_and_dial(type, format, data, timeout, reason, cid_num, cid_name, &oh);
+ chan = __ast_request_and_dial(type, format, NULL, data, timeout, reason, cid_num, cid_name, &oh);
if (channel) {
*channel = chan;
if (chan)
@@ -8260,7 +8268,7 @@ int ast_pbx_outgoing_exten(const char *type, int format, void *data, int timeout
/* create a fake channel and execute the "failed" extension (if it exists) within the requested context */
/* check if "failed" exists */
if (ast_exists_extension(chan, context, "failed", 1, NULL)) {
- chan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "OutgoingSpoolFailed");
+ chan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", NULL, 0, "OutgoingSpoolFailed");
if (chan) {
char failed_reason[4] = "";
if (!ast_strlen_zero(context))
@@ -8284,7 +8292,7 @@ int ast_pbx_outgoing_exten(const char *type, int format, void *data, int timeout
res = -1;
goto outgoing_exten_cleanup;
}
- chan = ast_request_and_dial(type, format, data, timeout, reason, cid_num, cid_name);
+ chan = ast_request_and_dial(type, format, NULL, data, timeout, reason, cid_num, cid_name);
if (channel) {
*channel = chan;
if (chan)
@@ -8361,7 +8369,7 @@ int ast_pbx_outgoing_app(const char *type, int format, void *data, int timeout,
goto outgoing_app_cleanup;
}
if (synchronous) {
- chan = __ast_request_and_dial(type, format, data, timeout, reason, cid_num, cid_name, &oh);
+ chan = __ast_request_and_dial(type, format, NULL, data, timeout, reason, cid_num, cid_name, &oh);
if (chan) {
ast_set_variables(chan, vars);
if (account)
@@ -8426,7 +8434,7 @@ int ast_pbx_outgoing_app(const char *type, int format, void *data, int timeout,
res = -1;
goto outgoing_app_cleanup;
}
- chan = __ast_request_and_dial(type, format, data, timeout, reason, cid_num, cid_name, &oh);
+ chan = __ast_request_and_dial(type, format, NULL, data, timeout, reason, cid_num, cid_name, &oh);
if (!chan) {
ast_free(as);
res = -1;
@@ -8833,6 +8841,8 @@ static int pbx_builtin_setamaflags(struct ast_channel *chan, const char *data)
*/
static int pbx_builtin_hangup(struct ast_channel *chan, const char *data)
{
+ ast_set_hangupsource(chan, "dialplan/builtin", 0);
+
if (!ast_strlen_zero(data)) {
int cause;
char *endptr;
@@ -9549,7 +9559,7 @@ int load_pbx(void)
/* Register manager application */
ast_manager_register_xml("ShowDialPlan", EVENT_FLAG_CONFIG | EVENT_FLAG_REPORTING, manager_show_dialplan);
- if (!(device_state_sub = ast_event_subscribe(AST_EVENT_DEVICE_STATE, device_state_cb, NULL,
+ if (!(device_state_sub = ast_event_subscribe(AST_EVENT_DEVICE_STATE_CHANGE, device_state_cb, "pbx Device State Change", NULL,
AST_EVENT_IE_END))) {
return -1;
}