diff options
Diffstat (limited to 'main')
-rw-r--r-- | main/asterisk.c | 6 | ||||
-rw-r--r-- | main/cdr.c | 62 | ||||
-rw-r--r-- | main/cel.c | 652 | ||||
-rw-r--r-- | main/channel.c | 333 | ||||
-rw-r--r-- | main/cli.c | 13 | ||||
-rw-r--r-- | main/devicestate.c | 2 | ||||
-rw-r--r-- | main/dial.c | 2 | ||||
-rw-r--r-- | main/event.c | 64 | ||||
-rw-r--r-- | main/features.c | 70 | ||||
-rw-r--r-- | main/loader.c | 1 | ||||
-rw-r--r-- | main/logger.c | 2 | ||||
-rw-r--r-- | main/manager.c | 2 | ||||
-rw-r--r-- | main/pbx.c | 44 |
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; } |