dect
/
asterisk
Archived
13
0
Fork 0

Merge the new Channel Event Logging (CEL) subsystem.

CEL is the new system for logging channel events.  This was inspired after
facing many problems trying to represent what is possible to happen to a call
in Asterisk using CDR records.  For more information on CEL, see the built in
HTML or PDF documentation generated from the files in doc/tex/.

Many thanks to Steve Murphy (murf) and Brian Degenhardt (bmd) for their hard
work developing this code.  Also, thanks to Matt Nicholson (mnicholson) and
Sean Bright (seanbright) for their assistance in the final push to get this
code ready for Asterisk trunk.

Review: https://reviewboard.asterisk.org/r/239/


git-svn-id: http://svn.digium.com/svn/asterisk/trunk@203638 f38db490-d61c-443f-a65b-d21fe96a405b
This commit is contained in:
russell 2009-06-26 15:28:53 +00:00
parent d13a40e1cf
commit ac3b35dcc7
87 changed files with 6919 additions and 365 deletions

14
CHANGES
View File

@ -7,6 +7,7 @@
=== and the other UPGRADE files for older releases.
===
======================================================================
------------------------------------------------------------------------------
--- Functionality changes from Asterisk 1.6.2 to Asterisk 1.6.3 -------------
------------------------------------------------------------------------------
@ -161,8 +162,21 @@ Logger
users of this channel in the tree have been converted to LOG_NOTICE or removed
(in cases where the same message was already generated to another channel).
Channel Event Logging
---------------------
* A new interface, CEL, is introduced here. CEL logs single events, much like
the AMI, but it differs from the AMI in that it logs to db backends much
like CDR does; is based on the event subsystem introduced by Russell, and
can share in all its benefits; allows multiple backends to operate like CDR;
is specialized to event data that would be of concern to billing sytems,
like CDR. Backends for logging and accounting calls have been produced,
but a new CDR backend is still in development.
CDR
---
* 'linkedid' and 'peeraccount' are new CDR fields available to CDR officianados.
linkedid is based on uniqueID, but spreads to other channels as transfers, dials,
etc are performed. Thus the peices of CDR can be grouped into multilegged sets.
* Multiple files and formats can now be specified in cdr_custom.conf.
Calendaring for Asterisk

View File

@ -293,7 +293,7 @@ endif
# value directly to ASTCFLAGS
ASTCFLAGS+=$(MALLOC_DEBUG)$(OPTIONS)
MOD_SUBDIRS:=channels pbx apps codecs formats cdr bridges funcs tests main res $(LOCAL_MOD_SUBDIRS)
MOD_SUBDIRS:=channels pbx apps codecs formats cdr cel bridges funcs tests main res $(LOCAL_MOD_SUBDIRS)
OTHER_SUBDIRS:=utils agi
SUBDIRS:=$(OTHER_SUBDIRS) $(MOD_SUBDIRS)
SUBDIRS_INSTALL:=$(SUBDIRS:%=%-install)
@ -574,6 +574,8 @@ bininstall: _all installdirs $(SUBDIRS_INSTALL)
mkdir -p $(DESTDIR)$(ASTDATADIR)/documentation/thirdparty
mkdir -p $(DESTDIR)$(ASTLOGDIR)/cdr-csv
mkdir -p $(DESTDIR)$(ASTLOGDIR)/cdr-custom
mkdir -p $(DESTDIR)$(ASTLOGDIR)/cel-csv
mkdir -p $(DESTDIR)$(ASTLOGDIR)/cel-custom
mkdir -p $(DESTDIR)$(ASTDATADIR)/keys
mkdir -p $(DESTDIR)$(ASTDATADIR)/firmware
mkdir -p $(DESTDIR)$(ASTDATADIR)/firmware/iax

View File

@ -0,0 +1,98 @@
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 2008, 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 Generate User-Defined CEL event
*
* \author Steve Murphy
*
* \ingroup applications
*/
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/module.h"
#include "asterisk/app.h"
#include "asterisk/channel.h"
#include "asterisk/cel.h"
/*** DOCUMENTATION
<application name="CELGenUserEvent" language="en_US">
<synopsis>
Generates a CEL User Defined Event.
</synopsis>
<syntax>
<parameter name="event-name" required="true">
<argument name="event-name" required="true">
</argument>
</parameter>
</syntax>
<description>
<para>A CEL event will be immediately generated by this channel, with the supplied name for a type.</para>
</description>
</application>
***/
static char *app = "CELGenUserEvent";
static int celgenuserevent_exec(struct ast_channel *chan, const char *data)
{
int res = 0;
char *parse;
AST_DECLARE_APP_ARGS(args,
AST_APP_ARG(event);
AST_APP_ARG(extra);
);
if (ast_strlen_zero(data)) {
return 0;
}
parse = ast_strdupa(data);
AST_STANDARD_APP_ARGS(args, parse);
ast_cel_report_event(chan, AST_CEL_USER_DEFINED, args.event, args.extra, NULL);
return res;
}
static int unload_module(void)
{
int res;
res = ast_unregister_application(app);
ast_module_user_hangup_all();
return res;
}
static int load_module(void)
{
int res = ast_register_application_xml(app, celgenuserevent_exec);
if (res) {
return AST_MODULE_LOAD_DECLINE;
} else {
return AST_MODULE_LOAD_SUCCESS;
}
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Generate an User-Defined CEL event",
.load = load_module,
.unload = unload_module,
);

View File

@ -159,7 +159,7 @@ static int chanavail_exec(struct ast_channel *chan, const char *data)
snprintf(trychan, sizeof(trychan), "%s/%s",cur,number);
status = inuse = ast_device_state(trychan);
}
if ((inuse <= 1) && (tempchan = ast_request(tech, chan->nativeformats, number, &status))) {
if ((inuse <= 1) && (tempchan = ast_request(tech, chan->nativeformats, chan, number, &status))) {
ast_str_append(&tmp_availchan, 0, "%s%s", ast_str_strlen(tmp_availchan) ? "&" : "", tempchan->name);
snprintf(tmp, sizeof(tmp), "%s/%s", tech, number);

View File

@ -559,7 +559,7 @@ static int play_sound_file(struct conference_bridge *conference_bridge, const ch
if (!(conference_bridge->playback_chan)) {
int cause;
if (!(conference_bridge->playback_chan = ast_request("Bridge", AST_FORMAT_SLINEAR, "", &cause))) {
if (!(conference_bridge->playback_chan = ast_request("Bridge", AST_FORMAT_SLINEAR, NULL, "", &cause))) {
ast_mutex_unlock(&conference_bridge->playback_lock);
return -1;
}

View File

@ -61,6 +61,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/stringfields.h"
#include "asterisk/global_datastores.h"
#include "asterisk/dsp.h"
#include "asterisk/cel.h"
/*** DOCUMENTATION
<application name="Dial" language="en_US">
@ -756,6 +757,9 @@ static void do_forward(struct chanlist *o,
stuff = tmpchan;
tech = "Local";
}
ast_cel_report_event(in, AST_CEL_FORWARD, NULL, c->call_forward, NULL);
/* Before processing channel, go ahead and check for forwarding */
ast_verb(3, "Now forwarding %s to '%s/%s' (thanks to %s)\n", in->name, tech, stuff, c->name);
/* If we have been told to ignore forwards, just set this channel to null and continue processing extensions normally */
@ -765,7 +769,7 @@ static void do_forward(struct chanlist *o,
cause = AST_CAUSE_BUSY;
} else {
/* Setup parameters */
c = o->chan = ast_request(tech, in->nativeformats, stuff, &cause);
c = o->chan = ast_request(tech, in->nativeformats, in, stuff, &cause);
if (c) {
if (single)
ast_channel_make_compatible(o->chan, in);
@ -1872,7 +1876,7 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
AST_LIST_UNLOCK(dialed_interfaces);
}
tc = ast_request(tech, chan->nativeformats, numsubst, &cause);
tc = ast_request(tech, chan->nativeformats, chan, numsubst, &cause);
if (!tc) {
/* If we can't, just go on to the next call */
ast_log(LOG_WARNING, "Unable to create channel of type '%s' (cause %d - %s)\n",
@ -1921,7 +1925,9 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
tc->cid.cid_tns = chan->cid.cid_tns;
ast_string_field_set(tc, accountcode, chan->accountcode);
if (!ast_strlen_zero(chan->accountcode)) {
ast_string_field_set(tc, peeraccount, chan->accountcode);
}
tc->cdrflags = chan->cdrflags;
if (ast_strlen_zero(tc->musicclass))
ast_string_field_set(tc, musicclass, chan->musicclass);

View File

@ -41,6 +41,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/app.h"
#include "asterisk/features.h"
#include "asterisk/callerid.h"
#include "asterisk/cel.h"
#define PICKUPMARK "PICKUPMARK"
@ -95,6 +96,7 @@ static int pickup_do(struct ast_channel *chan, struct ast_channel *target)
struct ast_party_connected_line connected_caller;
ast_debug(1, "Call pickup on '%s' by '%s'\n", target->name, chan->name);
ast_cel_report_event(target, AST_CEL_PICKUP, NULL, NULL, chan);
connected_caller = target->connected;
connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;

View File

@ -831,7 +831,7 @@ static void findmeexec(struct fm_args *tpargs)
return;
}
outbound = ast_request("Local", ast_best_codec(caller->nativeformats), dialarg, &dg);
outbound = ast_request("Local", ast_best_codec(caller->nativeformats), caller, dialarg, &dg);
if (outbound) {
ast_set_callerid(outbound, caller->cid.cid_num, caller->cid.cid_name, caller->cid.cid_num);
ast_channel_inherit_variables(tpargs->chan, outbound);

View File

@ -1176,7 +1176,7 @@ static struct ast_conference *build_conf(char *confno, char *pin, char *pinadmin
cnf->dahdiconf = dahdic.confno;
/* Setup a new channel for playback of audio files */
cnf->chan = ast_request("DAHDI", AST_FORMAT_SLINEAR, "pseudo", NULL);
cnf->chan = ast_request("DAHDI", AST_FORMAT_SLINEAR, chan, "pseudo", NULL);
if (cnf->chan) {
ast_set_read_format(cnf->chan, AST_FORMAT_SLINEAR);
ast_set_write_format(cnf->chan, AST_FORMAT_SLINEAR);
@ -2202,7 +2202,7 @@ static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int c
}
ast_mutex_lock(&conf->recordthreadlock);
if ((conf->recordthread == AST_PTHREADT_NULL) && (confflags & CONFFLAG_RECORDCONF) && ((conf->lchan = ast_request("DAHDI", AST_FORMAT_SLINEAR, "pseudo", NULL)))) {
if ((conf->recordthread == AST_PTHREADT_NULL) && (confflags & CONFFLAG_RECORDCONF) && ((conf->lchan = ast_request("DAHDI", AST_FORMAT_SLINEAR, chan, "pseudo", NULL)))) {
ast_set_read_format(conf->lchan, AST_FORMAT_SLINEAR);
ast_set_write_format(conf->lchan, AST_FORMAT_SLINEAR);
dahdic.chan = 0;

View File

@ -1299,8 +1299,7 @@ static int sendmail(struct minivm_template *template, struct minivm_account *vmu
return -1;
}
/* Allocate channel used for chanvar substitution */
ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "%s", "");
ast = ast_dummy_channel_alloc();
snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
@ -1461,9 +1460,8 @@ static int sendmail(struct minivm_template *template, struct minivm_account *vmu
ast_safe_system(tmp2);
ast_debug(1, "Sent message to %s with command '%s' - %s\n", vmu->email, global_mailcmd, template->attachment ? "(media attachment)" : "");
ast_debug(3, "Actual command used: %s\n", tmp2);
if (ast) {
if (ast)
ast = ast_channel_release(ast);
}
ast_free(str1);
ast_free(str2);
return 0;

View File

@ -145,7 +145,7 @@ static int parkandannounce_exec(struct ast_channel *chan, const char *data)
snprintf(buf, sizeof(buf), "%d", lot);
oh.parent_channel = chan;
oh.vars = ast_variable_new("_PARKEDAT", buf, "");
dchan = __ast_request_and_dial(dialtech, AST_FORMAT_SLINEAR, args.dial, 30000, &outstate, chan->cid.cid_num, chan->cid.cid_name, &oh);
dchan = __ast_request_and_dial(dialtech, AST_FORMAT_SLINEAR, chan, args.dial, 30000, &outstate, chan->cid.cid_num, chan->cid.cid_name, &oh);
if (dchan) {
if (dchan->_state == AST_STATE_UP) {

View File

@ -95,6 +95,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/global_datastores.h"
#include "asterisk/taskprocessor.h"
#include "asterisk/callerid.h"
#include "asterisk/cel.h"
/*!
* \par Please read before modifying this file.
@ -2656,7 +2657,7 @@ static int ring_entry(struct queue_ent *qe, struct callattempt *tmp, int *busies
location = "";
/* Request the peer */
tmp->chan = ast_request(tech, qe->chan->nativeformats, location, &status);
tmp->chan = ast_request(tech, qe->chan->nativeformats, qe->chan, location, &status);
if (!tmp->chan) { /* If we can't, just go on to the next call */
if (qe->chan->cdr)
ast_cdr_busy(qe->chan->cdr);
@ -3138,10 +3139,13 @@ static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callatte
stuff = tmpchan;
tech = "Local";
}
ast_cel_report_event(in, AST_CEL_FORWARD, NULL, o->chan->call_forward, NULL);
/* Before processing channel, go ahead and check for forwarding */
ast_verb(3, "Now forwarding %s to '%s/%s' (thanks to %s)\n", inchan_name, tech, stuff, ochan_name);
/* Setup parameters */
o->chan = ast_request(tech, in->nativeformats, stuff, &status);
o->chan = ast_request(tech, in->nativeformats, in, stuff, &status);
if (!o->chan) {
ast_log(LOG_NOTICE, "Unable to create local channel for call forward to '%s/%s'\n", tech, stuff);
o->stillgoing = 0;
@ -7483,7 +7487,8 @@ static int load_module(void)
ast_log(LOG_WARNING, "devicestate taskprocessor reference failed - devicestate notifications will not occur\n");
}
if (!(device_state_sub = ast_event_subscribe(AST_EVENT_DEVICE_STATE, device_state_cb, NULL, AST_EVENT_IE_END))) {
/* in the following subscribe call, do I use DEVICE_STATE, or DEVICE_STATE_CHANGE? */
if (!(device_state_sub = ast_event_subscribe(AST_EVENT_DEVICE_STATE, device_state_cb, "AppQueue Device state", NULL, AST_EVENT_IE_END))) {
res = -1;
}

View File

@ -4105,7 +4105,7 @@ static void make_email_file(FILE *p, char *srcemail, struct ast_vm_user *vmu, in
if (!ast_strlen_zero(fromstring)) {
struct ast_channel *ast;
if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
if ((ast = ast_dummy_channel_alloc())) {
char *ptr;
prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, category, flag);
ast_str_substitute_variables(&str1, 0, ast, fromstring);
@ -4151,7 +4151,7 @@ static void make_email_file(FILE *p, char *srcemail, struct ast_vm_user *vmu, in
if (!ast_strlen_zero(emailsubject) || !ast_strlen_zero(vmu->emailsubject)) {
char *e_subj = !ast_strlen_zero(vmu->emailsubject) ? vmu->emailsubject : emailsubject;
struct ast_channel *ast;
if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
if ((ast = ast_dummy_channel_alloc())) {
prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
ast_str_substitute_variables(&str1, 0, ast, e_subj);
if (check_mime(ast_str_buffer(str1))) {
@ -4234,7 +4234,7 @@ static void make_email_file(FILE *p, char *srcemail, struct ast_vm_user *vmu, in
if (emailbody || vmu->emailbody) {
char* e_body = vmu->emailbody ? vmu->emailbody : emailbody;
struct ast_channel *ast;
if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
if ((ast = ast_dummy_channel_alloc())) {
prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
ast_str_substitute_variables(&str1, 0, ast, e_body);
fprintf(p, "%s" ENDL, ast_str_buffer(str1));
@ -4434,7 +4434,7 @@ static int sendpage(char *srcemail, char *pager, int msgnum, char *context, char
if (!ast_strlen_zero(pagerfromstring)) {
struct ast_channel *ast;
if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
if ((ast = ast_dummy_channel_alloc())) {
char *ptr;
prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, category, flag);
ast_str_substitute_variables(&str1, 0, ast, pagerfromstring);
@ -4479,7 +4479,7 @@ static int sendpage(char *srcemail, char *pager, int msgnum, char *context, char
if (!ast_strlen_zero(pagersubject)) {
struct ast_channel *ast;
if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
if ((ast = ast_dummy_channel_alloc())) {
prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
ast_str_substitute_variables(&str1, 0, ast, pagersubject);
if (check_mime(ast_str_buffer(str1))) {
@ -4512,7 +4512,7 @@ static int sendpage(char *srcemail, char *pager, int msgnum, char *context, char
ast_strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
if (pagerbody) {
struct ast_channel *ast;
if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
if ((ast = ast_dummy_channel_alloc())) {
prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
ast_str_substitute_variables(&str1, 0, ast, pagerbody);
fprintf(p, "%s" ENDL, ast_str_buffer(str1));
@ -10254,11 +10254,11 @@ static void mwi_sub_event_cb(const struct ast_event *event, void *userdata)
static void start_poll_thread(void)
{
mwi_sub_sub = ast_event_subscribe(AST_EVENT_SUB, mwi_sub_event_cb, NULL,
mwi_sub_sub = ast_event_subscribe(AST_EVENT_SUB, mwi_sub_event_cb, "Voicemail MWI subscription", NULL,
AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, AST_EVENT_MWI,
AST_EVENT_IE_END);
mwi_unsub_sub = ast_event_subscribe(AST_EVENT_UNSUB, mwi_unsub_event_cb, NULL,
mwi_unsub_sub = ast_event_subscribe(AST_EVENT_UNSUB, mwi_unsub_event_cb, "Voicemail MWI subscription", NULL,
AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, AST_EVENT_MWI,
AST_EVENT_IE_END);

View File

@ -75,7 +75,7 @@ static struct ast_channel *dial_transfer(const struct ast_channel *caller, const
snprintf(destination, sizeof(destination), "%s@%s", exten, context);
/* Now we request that chan_local prepare to call the destination */
if (!(chan = ast_request("Local", caller->nativeformats, destination, &cause))) {
if (!(chan = ast_request("Local", caller->nativeformats, caller, destination, &cause))) {
return NULL;
}

View File

@ -124,7 +124,7 @@ static int custom_log(struct ast_cdr *cdr)
return -1;
}
dummy = ast_channel_alloc(0, 0, "", "", "", "", "", 0, "Substitution/%p", cdr);
dummy = ast_dummy_channel_alloc();
if (!dummy) {
ast_log(LOG_ERROR, "Unable to allocate channel for variable subsitution.\n");

View File

@ -156,7 +156,7 @@ static int manager_log(struct ast_cdr *cdr)
buf[0] = '\0';
ast_rwlock_rdlock(&customfields_lock);
if (customfields && ast_str_strlen(customfields)) {
struct ast_channel *dummy = ast_channel_alloc(0, 0, "", "", "", "", "", 0, "Substitution/%p", cdr);
struct ast_channel *dummy = ast_dummy_channel_alloc();
if (!dummy) {
ast_log(LOG_ERROR, "Unable to allocate channel for variable substitution.\n");
return 0;

View File

@ -184,7 +184,7 @@ static int load_module(void)
if (!db) {
ast_log(LOG_ERROR, "cdr_sqlite: %s\n", zErr);
ast_free(zErr);
return -1;
return AST_MODULE_LOAD_DECLINE;
}
/* is the table there? */
@ -203,14 +203,14 @@ static int load_module(void)
res = ast_cdr_register(name, ast_module_info->description, sqlite_log);
if (res) {
ast_log(LOG_ERROR, "Unable to register SQLite CDR handling\n");
return -1;
return AST_MODULE_LOAD_DECLINE;
}
return 0;
return AST_MODULE_LOAD_SUCCESS;
err:
if (db)
sqlite_close(db);
return -1;
return AST_MODULE_LOAD_DECLINE;
}
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "SQLite CDR Backend");

View File

@ -241,7 +241,7 @@ static int sqlite3_log(struct ast_cdr *cdr)
struct ast_channel *dummy;
struct ast_str *value_string = ast_str_create(1024);
dummy = ast_channel_alloc(0, 0, "", "", "", "", "", 0, "Substitution/%p", cdr);
dummy = ast_dummy_channel_alloc();
if (!dummy) {
ast_log(LOG_ERROR, "Unable to allocate channel for variable subsitution.\n");
ast_free(value_string);

20
cel/Makefile Normal file
View File

@ -0,0 +1,20 @@
#
# Asterisk -- A telephony toolkit for Linux.
#
# Makefile for CEL backends
#
# Copyright (C) 1999-2008, Digium, Inc.
#
# This program is free software, distributed under the terms of
# the GNU General Public License
#
-include $(ASTTOPDIR)/menuselect.makeopts $(ASTTOPDIR)/menuselect.makedeps
MODULE_PREFIX=cel
MENUSELECT_CATEGORY=CEL
MENUSELECT_DESCRIPTION=Channel Event Logging
all: _all
include $(ASTTOPDIR)/Makefile.moddir_rules

771
cel/cel_adaptive_odbc.c Normal file
View File

@ -0,0 +1,771 @@
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 2008 Digium
*
* Adapted from cdr_adaptive_odbc:
* Tilghman Lesher <cdr_adaptive_odbc__v1@the-tilghman.com>
* by Steve Murphy
*
* 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 Adaptive ODBC CEL backend
*
* \author Tilghman Lesher <cdr_adaptive_odbc__v1@the-tilghman.com>
* \ingroup cel_drivers
*/
/*** MODULEINFO
<depend>generic_odbc</depend>
<depend>ltdl</depend>
***/
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include <sys/types.h>
#include <time.h>
#include <sql.h>
#include <sqlext.h>
#include <sqltypes.h>
#include "asterisk/config.h"
#include "asterisk/channel.h"
#include "asterisk/lock.h"
#include "asterisk/linkedlists.h"
#include "asterisk/res_odbc.h"
#include "asterisk/cel.h"
#include "asterisk/module.h"
#define CONFIG "cel_adaptive_odbc.conf"
static struct ast_event_sub *event_sub = NULL;
/* Optimization to reduce number of memory allocations */
static int maxsize = 512, maxsize2 = 512;
struct columns {
char *name;
char *celname;
char *filtervalue;
char *staticvalue;
SQLSMALLINT type;
SQLINTEGER size;
SQLSMALLINT decimals;
SQLSMALLINT radix;
SQLSMALLINT nullable;
SQLINTEGER octetlen;
AST_LIST_ENTRY(columns) list;
};
struct tables {
char *connection;
char *table;
unsigned int usegmtime:1;
AST_LIST_HEAD_NOLOCK(odbc_columns, columns) columns;
AST_RWLIST_ENTRY(tables) list;
};
static AST_RWLIST_HEAD_STATIC(odbc_tables, tables);
static int load_config(void)
{
struct ast_config *cfg;
struct ast_variable *var;
const char *tmp, *catg;
struct tables *tableptr;
struct columns *entry;
struct odbc_obj *obj;
char columnname[80];
char connection[40];
char table[40];
int lenconnection, lentable, usegmtime = 0;
SQLLEN sqlptr;
int res = 0;
SQLHSTMT stmt = NULL;
struct ast_flags config_flags = { 0 }; /* Part of our config comes from the database */
cfg = ast_config_load(CONFIG, config_flags);
if (!cfg || cfg == CONFIG_STATUS_FILEINVALID) {
ast_log(LOG_WARNING, "Unable to load " CONFIG ". No adaptive ODBC CEL records!\n");
return -1;
}
for (catg = ast_category_browse(cfg, NULL); catg; catg = ast_category_browse(cfg, catg)) {
var = ast_variable_browse(cfg, catg);
if (!var)
continue;
if (ast_strlen_zero(tmp = ast_variable_retrieve(cfg, catg, "connection"))) {
ast_log(LOG_WARNING, "No connection parameter found in '%s'. Skipping.\n", catg);
continue;
}
ast_copy_string(connection, tmp, sizeof(connection));
lenconnection = strlen(connection);
if (!ast_strlen_zero(tmp = ast_variable_retrieve(cfg, catg, "usegmtime"))) {
usegmtime = ast_true(tmp);
}
/* When loading, we want to be sure we can connect. */
obj = ast_odbc_request_obj(connection, 1);
if (!obj) {
ast_log(LOG_WARNING, "No such connection '%s' in the '%s' section of " CONFIG ". Check res_odbc.conf.\n", connection, catg);
continue;
}
if (ast_strlen_zero(tmp = ast_variable_retrieve(cfg, catg, "table"))) {
ast_log(LOG_NOTICE, "No table name found. Assuming 'cel'.\n");
tmp = "cel";
}
ast_copy_string(table, tmp, sizeof(table));
lentable = strlen(table);
res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
ast_log(LOG_WARNING, "SQL Alloc Handle failed on connection '%s'!\n", connection);
ast_odbc_release_obj(obj);
continue;
}
res = SQLColumns(stmt, NULL, 0, NULL, 0, (unsigned char *)table, SQL_NTS, (unsigned char *)"%", SQL_NTS);
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
ast_log(LOG_ERROR, "Unable to query database columns on connection '%s'. Skipping.\n", connection);
ast_odbc_release_obj(obj);
continue;
}
tableptr = ast_calloc(sizeof(char), sizeof(*tableptr) + lenconnection + 1 + lentable + 1);
if (!tableptr) {
ast_log(LOG_ERROR, "Out of memory creating entry for table '%s' on connection '%s'\n", table, connection);
ast_odbc_release_obj(obj);
res = -1;
break;
}
tableptr->usegmtime = usegmtime;
tableptr->connection = (char *)tableptr + sizeof(*tableptr);
tableptr->table = (char *)tableptr + sizeof(*tableptr) + lenconnection + 1;
ast_copy_string(tableptr->connection, connection, lenconnection + 1);
ast_copy_string(tableptr->table, table, lentable + 1);
ast_verb(3, "Found adaptive CEL table %s@%s.\n", tableptr->table, tableptr->connection);
/* Check for filters first */
for (var = ast_variable_browse(cfg, catg); var; var = var->next) {
if (strncmp(var->name, "filter", 6) == 0) {
char *celvar = ast_strdupa(var->name + 6);
celvar = ast_strip(celvar);
ast_verb(3, "Found filter %s for cel variable %s in %s@%s\n", var->value, celvar, tableptr->table, tableptr->connection);
entry = ast_calloc(sizeof(char), sizeof(*entry) + strlen(celvar) + 1 + strlen(var->value) + 1);
if (!entry) {
ast_log(LOG_ERROR, "Out of memory creating filter entry for CEL variable '%s' in table '%s' on connection '%s'\n", celvar, table, connection);
res = -1;
break;
}
/* NULL column entry means this isn't a column in the database */
entry->name = NULL;
entry->celname = (char *)entry + sizeof(*entry);
entry->filtervalue = (char *)entry + sizeof(*entry) + strlen(celvar) + 1;
strcpy(entry->celname, celvar);
strcpy(entry->filtervalue, var->value);
AST_LIST_INSERT_TAIL(&(tableptr->columns), entry, list);
}
}
while ((res = SQLFetch(stmt)) != SQL_NO_DATA && res != SQL_ERROR) {
char *celvar = "", *staticvalue = "";
SQLGetData(stmt, 4, SQL_C_CHAR, columnname, sizeof(columnname), &sqlptr);
/* Is there an alias for this column? */
/* NOTE: This seems like a non-optimal parse method, but I'm going
* for user configuration readability, rather than fast parsing. We
* really don't parse this file all that often, anyway.
*/
for (var = ast_variable_browse(cfg, catg); var; var = var->next) {
if (strncmp(var->name, "alias", 5) == 0 && strcasecmp(var->value, columnname) == 0) {
char *alias = ast_strdupa(var->name + 5);
celvar = ast_strip(alias);
ast_verb(3, "Found alias %s for column %s in %s@%s\n", celvar, columnname, tableptr->table, tableptr->connection);
break;
} else if (strncmp(var->name, "static", 6) == 0 && strcasecmp(var->value, columnname) == 0) {
char *item = ast_strdupa(var->name + 6);
item = ast_strip(item);
if (item[0] == '"' && item[strlen(item) - 1] == '"') {
/* Remove surrounding quotes */
item[strlen(item) - 1] = '\0';
item++;
}
staticvalue = item;
}
}
entry = ast_calloc(sizeof(char), sizeof(*entry) + strlen(columnname) + 1 + strlen(celvar) + 1 + strlen(staticvalue) + 1);
if (!entry) {
ast_log(LOG_ERROR, "Out of memory creating entry for column '%s' in table '%s' on connection '%s'\n", columnname, table, connection);
res = -1;
break;
}
entry->name = (char *)entry + sizeof(*entry);
strcpy(entry->name, columnname);
if (!ast_strlen_zero(celvar)) {
entry->celname = entry->name + strlen(columnname) + 1;
strcpy(entry->celname, celvar);
} else { /* Point to same place as the column name */
entry->celname = (char *)entry + sizeof(*entry);
}
if (!ast_strlen_zero(staticvalue)) {
entry->staticvalue = entry->celname + strlen(entry->celname) + 1;
strcpy(entry->staticvalue, staticvalue);
}
SQLGetData(stmt, 5, SQL_C_SHORT, &entry->type, sizeof(entry->type), NULL);
SQLGetData(stmt, 7, SQL_C_LONG, &entry->size, sizeof(entry->size), NULL);
SQLGetData(stmt, 9, SQL_C_SHORT, &entry->decimals, sizeof(entry->decimals), NULL);
SQLGetData(stmt, 10, SQL_C_SHORT, &entry->radix, sizeof(entry->radix), NULL);
SQLGetData(stmt, 11, SQL_C_SHORT, &entry->nullable, sizeof(entry->nullable), NULL);
SQLGetData(stmt, 16, SQL_C_LONG, &entry->octetlen, sizeof(entry->octetlen), NULL);
/* Specification states that the octenlen should be the maximum number of bytes
* returned in a char or binary column, but it seems that some drivers just set
* it to NULL. (Bad Postgres! No biscuit!) */
if (entry->octetlen == 0)
entry->octetlen = entry->size;
ast_verb(10, "Found %s column with type %hd with len %ld, octetlen %ld, and numlen (%hd,%hd)\n", entry->name, entry->type, (long) entry->size, (long) entry->octetlen, entry->decimals, entry->radix);
/* Insert column info into column list */
AST_LIST_INSERT_TAIL(&(tableptr->columns), entry, list);
res = 0;
}
SQLFreeHandle(SQL_HANDLE_STMT, stmt);
ast_odbc_release_obj(obj);
if (AST_LIST_FIRST(&(tableptr->columns)))
AST_RWLIST_INSERT_TAIL(&odbc_tables, tableptr, list);
else
ast_free(tableptr);
}
return res;
}
static int free_config(void)
{
struct tables *table;
struct columns *entry;
while ((table = AST_RWLIST_REMOVE_HEAD(&odbc_tables, list))) {
while ((entry = AST_LIST_REMOVE_HEAD(&(table->columns), list))) {
ast_free(entry);
}
ast_free(table);
}
return 0;
}
static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
{
int res, i;
char *sql = data;
SQLHSTMT stmt;
SQLINTEGER nativeerror = 0, numfields = 0;
SQLSMALLINT diagbytes = 0;
unsigned char state[10], diagnostic[256];
res = SQLAllocHandle (SQL_HANDLE_STMT, obj->con, &stmt);
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
return NULL;
}
res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
SQLGetDiagField(SQL_HANDLE_STMT, stmt, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
for (i = 0; i < numfields; i++) {
SQLGetDiagRec(SQL_HANDLE_STMT, stmt, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
ast_log(LOG_WARNING, "SQL Execute returned an error %d: %s: %s (%d)\n", res, state, diagnostic, diagbytes);
if (i > 10) {
ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields);
break;
}
}
SQLFreeHandle (SQL_HANDLE_STMT, stmt);
return NULL;
}
return stmt;
}
#define LENGTHEN_BUF1(size) \
do { \
/* Lengthen buffer, if necessary */ \
if (ast_str_strlen(sql) + size + 1 > ast_str_size(sql)) { \
if (ast_str_make_space(&sql, ((ast_str_size(sql) + size + 1) / 512 + 1) * 512) != 0) { \
ast_log(LOG_ERROR, "Unable to allocate sufficient memory. Insert CEL '%s:%s' failed.\n", tableptr->connection, tableptr->table); \
ast_free(sql); \
ast_free(sql2); \
AST_RWLIST_UNLOCK(&odbc_tables); \
return; \
} \
} \
} while (0)
#define LENGTHEN_BUF2(size) \
do { \
if (ast_str_strlen(sql2) + size + 1 > ast_str_size(sql2)) { \
if (ast_str_make_space(&sql2, ((ast_str_size(sql2) + size + 3) / 512 + 1) * 512) != 0) { \
ast_log(LOG_ERROR, "Unable to allocate sufficient memory. Insert CEL '%s:%s' failed.\n", tableptr->connection, tableptr->table); \
ast_free(sql); \
ast_free(sql2); \
AST_RWLIST_UNLOCK(&odbc_tables); \
return; \
} \
} \
} while (0)
static void odbc_log(const struct ast_event *event, void *userdata)
{
struct tables *tableptr;
struct columns *entry;
struct odbc_obj *obj;
struct ast_str *sql = ast_str_create(maxsize), *sql2 = ast_str_create(maxsize2);
char *tmp;
char colbuf[1024], *colptr;
SQLHSTMT stmt = NULL;
SQLLEN rows = 0;
struct ast_cel_event_record record = {
.version = AST_CEL_EVENT_RECORD_VERSION,
};
if (ast_cel_fill_record(event, &record)) {
return;
}
if (!sql || !sql2) {
if (sql)
ast_free(sql);
if (sql2)
ast_free(sql2);
return;
}
if (AST_RWLIST_RDLOCK(&odbc_tables)) {
ast_log(LOG_ERROR, "Unable to lock table list. Insert CEL(s) failed.\n");
ast_free(sql);
ast_free(sql2);
return;
}
AST_LIST_TRAVERSE(&odbc_tables, tableptr, list) {
int first = 1;
ast_str_set(&sql, 0, "INSERT INTO %s (", tableptr->table);
ast_str_set(&sql2, 0, " VALUES (");
/* No need to check the connection now; we'll handle any failure in prepare_and_execute */
if (!(obj = ast_odbc_request_obj(tableptr->connection, 0))) {
ast_log(LOG_WARNING, "cel_adaptive_odbc: Unable to retrieve database handle for '%s:%s'. CEL failed: %s\n", tableptr->connection, tableptr->table, ast_str_buffer(sql));
continue;
}
AST_LIST_TRAVERSE(&(tableptr->columns), entry, list) {
int datefield = 0;
if (strcasecmp(entry->celname, "eventtime") == 0) {
datefield = 1;
}
/* Check if we have a similarly named variable */
if (entry->staticvalue) {
colptr = ast_strdupa(entry->staticvalue);
} else if (datefield) {
struct timeval date_tv = record.event_time;
struct ast_tm tm = { 0, };
ast_localtime(&date_tv, &tm, tableptr->usegmtime ? "UTC" : NULL);
ast_strftime(colbuf, sizeof(colbuf), "%Y-%m-%d %H:%M:%S", &tm);
colptr = colbuf;
} else {
if (strcmp(entry->celname, "userdeftype") == 0) {
strncpy(colbuf, record.user_defined_name, sizeof(colbuf));
} else if (strcmp(entry->celname, "cid_name") == 0) {
strncpy(colbuf, record.caller_id_name, sizeof(colbuf));
} else if (strcmp(entry->celname, "cid_num") == 0) {
strncpy(colbuf, record.caller_id_num, sizeof(colbuf));
} else if (strcmp(entry->celname, "cid_ani") == 0) {
strncpy(colbuf, record.caller_id_ani, sizeof(colbuf));
} else if (strcmp(entry->celname, "cid_rdnis") == 0) {
strncpy(colbuf, record.caller_id_rdnis, sizeof(colbuf));
} else if (strcmp(entry->celname, "cid_dnid") == 0) {
strncpy(colbuf, record.caller_id_dnid, sizeof(colbuf));
} else if (strcmp(entry->celname, "exten") == 0) {
strncpy(colbuf, record.extension, sizeof(colbuf));
} else if (strcmp(entry->celname, "context") == 0) {
strncpy(colbuf, record.context, sizeof(colbuf));
} else if (strcmp(entry->celname, "channame") == 0) {
strncpy(colbuf, record.channel_name, sizeof(colbuf));
} else if (strcmp(entry->celname, "appname") == 0) {
strncpy(colbuf, record.application_name, sizeof(colbuf));
} else if (strcmp(entry->celname, "appdata") == 0) {
strncpy(colbuf, record.application_data, sizeof(colbuf));
} else if (strcmp(entry->celname, "accountcode") == 0) {
strncpy(colbuf, record.account_code, sizeof(colbuf));
} else if (strcmp(entry->celname, "peeraccount") == 0) {
strncpy(colbuf, record.peer_account, sizeof(colbuf));
} else if (strcmp(entry->celname, "uniqueid") == 0) {
strncpy(colbuf, record.unique_id, sizeof(colbuf));
} else if (strcmp(entry->celname, "linkedid") == 0) {
strncpy(colbuf, record.linked_id, sizeof(colbuf));
} else if (strcmp(entry->celname, "userfield") == 0) {
strncpy(colbuf, record.user_field, sizeof(colbuf));
} else if (strcmp(entry->celname, "peer") == 0) {
strncpy(colbuf, record.peer, sizeof(colbuf));
} else if (strcmp(entry->celname, "amaflags") == 0) {
snprintf(colbuf, sizeof(colbuf), "%d", record.amaflag);
} else {
colbuf[0] = 0;
}
colptr = colbuf;
}
if (colptr) {
/* Check first if the column filters this entry. Note that this
* is very specifically NOT ast_strlen_zero(), because the filter
* could legitimately specify that the field is blank, which is
* different from the field being unspecified (NULL). */
if (entry->filtervalue && strcasecmp(colptr, entry->filtervalue) != 0) {
ast_verb(4, "CEL column '%s' with value '%s' does not match filter of"
" '%s'. Cancelling this CEL.\n",
entry->celname, colptr, entry->filtervalue);
goto early_release;
}
/* Only a filter? */
if (ast_strlen_zero(entry->name))
continue;
LENGTHEN_BUF1(strlen(entry->name));
switch (entry->type) {
case SQL_CHAR:
case SQL_VARCHAR:
case SQL_LONGVARCHAR:
case SQL_BINARY:
case SQL_VARBINARY:
case SQL_LONGVARBINARY:
case SQL_GUID:
/* For these two field names, get the rendered form, instead of the raw
* form (but only when we're dealing with a character-based field).
*/
if (strcasecmp(entry->name, "eventtype") == 0) {
snprintf(colbuf, sizeof(colbuf), "%s", record.event_name);
}
/* Truncate too-long fields */
if (entry->type != SQL_GUID) {
if (strlen(colptr) > entry->octetlen) {
colptr[entry->octetlen] = '\0';
}
}
ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
LENGTHEN_BUF2(strlen(colptr));
/* Encode value, with escaping */
ast_str_append(&sql2, 0, "%s'", first ? "" : ",");
for (tmp = colptr; *tmp; tmp++) {
if (*tmp == '\'') {
ast_str_append(&sql2, 0, "''");
} else if (*tmp == '\\' && ast_odbc_backslash_is_escape(obj)) {
ast_str_append(&sql2, 0, "\\\\");
} else {
ast_str_append(&sql2, 0, "%c", *tmp);
}
}
ast_str_append(&sql2, 0, "'");
break;
case SQL_TYPE_DATE:
{
int year = 0, month = 0, day = 0;
if (sscanf(colptr, "%d-%d-%d", &year, &month, &day) != 3 || year <= 0 ||
month <= 0 || month > 12 || day < 0 || day > 31 ||
((month == 4 || month == 6 || month == 9 || month == 11) && day == 31) ||
(month == 2 && year % 400 == 0 && day > 29) ||
(month == 2 && year % 100 == 0 && day > 28) ||
(month == 2 && year % 4 == 0 && day > 29) ||
(month == 2 && year % 4 != 0 && day > 28)) {
ast_log(LOG_WARNING, "CEL variable %s is not a valid date ('%s').\n", entry->name, colptr);
continue;
}
if (year > 0 && year < 100) {
year += 2000;
}
ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
LENGTHEN_BUF2(17);
ast_str_append(&sql2, 0, "%s{ d '%04d-%02d-%02d' }", first ? "" : ",", year, month, day);
}
break;
case SQL_TYPE_TIME:
{
int hour = 0, minute = 0, second = 0;
int count = sscanf(colptr, "%d:%d:%d", &hour, &minute, &second);
if ((count != 2 && count != 3) || hour < 0 || hour > 23 || minute < 0 || minute > 59 || second < 0 || second > 59) {
ast_log(LOG_WARNING, "CEL variable %s is not a valid time ('%s').\n", entry->name, colptr);
continue;
}
ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
LENGTHEN_BUF2(15);
ast_str_append(&sql2, 0, "%s{ t '%02d:%02d:%02d' }", first ? "" : ",", hour, minute, second);
}
break;
case SQL_TYPE_TIMESTAMP:
case SQL_TIMESTAMP:
{
int year = 0, month = 0, day = 0, hour = 0, minute = 0, second = 0;
int count = sscanf(colptr, "%d-%d-%d %d:%d:%d", &year, &month, &day, &hour, &minute, &second);
if ((count != 3 && count != 5 && count != 6) || year <= 0 ||
month <= 0 || month > 12 || day < 0 || day > 31 ||
((month == 4 || month == 6 || month == 9 || month == 11) && day == 31) ||
(month == 2 && year % 400 == 0 && day > 29) ||
(month == 2 && year % 100 == 0 && day > 28) ||
(month == 2 && year % 4 == 0 && day > 29) ||
(month == 2 && year % 4 != 0 && day > 28) ||
hour > 23 || minute > 59 || second > 59 || hour < 0 || minute < 0 || second < 0) {
ast_log(LOG_WARNING, "CEL variable %s is not a valid timestamp ('%s').\n", entry->name, colptr);
continue;
}
if (year > 0 && year < 100) {
year += 2000;
}
ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
LENGTHEN_BUF2(26);
ast_str_append(&sql2, 0, "%s{ ts '%04d-%02d-%02d %02d:%02d:%02d' }", first ? "" : ",", year, month, day, hour, minute, second);
}
break;
case SQL_INTEGER:
{
int integer = 0;
if (strcasecmp(entry->name, "eventtype") == 0) {
integer = (int) record.event_type;
} else if (sscanf(colptr, "%d", &integer) != 1) {
ast_log(LOG_WARNING, "CEL variable %s is not an integer.\n", entry->name);
continue;
}
ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
LENGTHEN_BUF2(12);
ast_str_append(&sql2, 0, "%s%d", first ? "" : ",", integer);
}
break;
case SQL_BIGINT:
{
long long integer = 0;
if (strcasecmp(entry->name, "eventtype") == 0) {
integer = (long long) record.event_type;
} else if (sscanf(colptr, "%lld", &integer) != 1) {
ast_log(LOG_WARNING, "CEL variable %s is not an integer.\n", entry->name);
continue;
}
ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
LENGTHEN_BUF2(24);
ast_str_append(&sql2, 0, "%s%lld", first ? "" : ",", integer);
}
break;
case SQL_SMALLINT:
{
short integer = 0;
if (strcasecmp(entry->name, "eventtype") == 0) {
integer = (short) record.event_type;
} else if (sscanf(colptr, "%hd", &integer) != 1) {
ast_log(LOG_WARNING, "CEL variable %s is not an integer.\n", entry->name);
continue;
}
ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
LENGTHEN_BUF2(6);
ast_str_append(&sql2, 0, "%s%d", first ? "" : ",", integer);
}
break;
case SQL_TINYINT:
{
char integer = 0;
if (strcasecmp(entry->name, "eventtype") == 0) {
integer = (char) record.event_type;
} else if (sscanf(colptr, "%hhd", &integer) != 1) {
ast_log(LOG_WARNING, "CEL variable %s is not an integer.\n", entry->name);
continue;
}
ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
LENGTHEN_BUF2(4);
ast_str_append(&sql2, 0, "%s%d", first ? "" : ",", integer);
}
break;
case SQL_BIT:
{
char integer = 0;
if (strcasecmp(entry->name, "eventtype") == 0) {
integer = (char) record.event_type;
} else if (sscanf(colptr, "%hhd", &integer) != 1) {
ast_log(LOG_WARNING, "CEL variable %s is not an integer.\n", entry->name);
continue;
}
if (integer != 0)
integer = 1;
ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
LENGTHEN_BUF2(2);
ast_str_append(&sql2, 0, "%s%d", first ? "" : ",", integer);
}
break;
case SQL_NUMERIC:
case SQL_DECIMAL:
{
double number = 0.0;
if (strcasecmp(entry->name, "eventtype") == 0) {
number = (double)record.event_type;
} else if (sscanf(colptr, "%lf", &number) != 1) {
ast_log(LOG_WARNING, "CEL variable %s is not an numeric type.\n", entry->name);
continue;
}
ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
LENGTHEN_BUF2(entry->decimals);
ast_str_append(&sql2, 0, "%s%*.*lf", first ? "" : ",", entry->decimals, entry->radix, number);
}
break;
case SQL_FLOAT:
case SQL_REAL:
case SQL_DOUBLE:
{
double number = 0.0;
if (strcasecmp(entry->name, "eventtype") == 0) {
number = (double) record.event_type;
} else if (sscanf(colptr, "%lf", &number) != 1) {
ast_log(LOG_WARNING, "CEL variable %s is not an numeric type.\n", entry->name);
continue;
}
ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
LENGTHEN_BUF2(entry->decimals);
ast_str_append(&sql2, 0, "%s%lf", first ? "" : ",", number);
}
break;
default:
ast_log(LOG_WARNING, "Column type %d (field '%s:%s:%s') is unsupported at this time.\n", entry->type, tableptr->connection, tableptr->table, entry->name);
continue;
}
first = 0;
}
}
/* Concatenate the two constructed buffers */
LENGTHEN_BUF1(ast_str_strlen(sql2));
ast_str_append(&sql, 0, ")");
ast_str_append(&sql2, 0, ")");
ast_str_append(&sql, 0, "%s", ast_str_buffer(sql2));
ast_verb(11, "[%s]\n", ast_str_buffer(sql));
stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, ast_str_buffer(sql));
if (stmt) {
SQLRowCount(stmt, &rows);
SQLFreeHandle(SQL_HANDLE_STMT, stmt);
}
if (rows == 0) {
ast_log(LOG_WARNING, "cel_adaptive_odbc: Insert failed on '%s:%s'. CEL failed: %s\n", tableptr->connection, tableptr->table, ast_str_buffer(sql));
}
early_release:
ast_odbc_release_obj(obj);
}
AST_RWLIST_UNLOCK(&odbc_tables);
/* Next time, just allocate buffers that are that big to start with. */
if (ast_str_strlen(sql) > maxsize) {
maxsize = ast_str_strlen(sql);
}
if (ast_str_strlen(sql2) > maxsize2) {
maxsize2 = ast_str_strlen(sql2);
}
ast_free(sql);
ast_free(sql2);
}
static int unload_module(void)
{
if (event_sub) {
event_sub = ast_event_unsubscribe(event_sub);
}
if (AST_RWLIST_WRLOCK(&odbc_tables)) {
event_sub = ast_event_subscribe(AST_EVENT_CEL, odbc_log, "Adaptive ODBC CEL backend", NULL, AST_EVENT_IE_END);
if (!event_sub) {
ast_log(LOG_ERROR, "cel_adaptive_odbc: Unable to subscribe to CEL events\n");
}
ast_log(LOG_ERROR, "Unable to lock column list. Unload failed.\n");
return -1;
}
free_config();
AST_RWLIST_UNLOCK(&odbc_tables);
return 0;
}
static int load_module(void)
{
if (AST_RWLIST_WRLOCK(&odbc_tables)) {
ast_log(LOG_ERROR, "Unable to lock column list. Load failed.\n");
return 0;
}
load_config();
AST_RWLIST_UNLOCK(&odbc_tables);
event_sub = ast_event_subscribe(AST_EVENT_CEL, odbc_log, "Adaptive ODBC CEL backend", NULL, AST_EVENT_IE_END);
if (!event_sub) {
ast_log(LOG_ERROR, "cel_odbc: Unable to subscribe to CEL events\n");
}
return AST_MODULE_LOAD_SUCCESS;
}
static int reload(void)
{
if (AST_RWLIST_WRLOCK(&odbc_tables)) {
ast_log(LOG_ERROR, "Unable to lock column list. Reload failed.\n");
return -1;
}
free_config();
load_config();
AST_RWLIST_UNLOCK(&odbc_tables);
return AST_MODULE_LOAD_SUCCESS;
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Adaptive ODBC CEL backend",
.load = load_module,
.unload = unload_module,
.reload = reload,
);

216
cel/cel_custom.c Normal file
View File

@ -0,0 +1,216 @@
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 2009, Digium, Inc.
*
* Steve Murphy <murf@digium.com>
* much borrowed from cdr code (cdr_custom.c), author Mark Spencer
*
* 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 Custom Comma Separated Value CEL records.
*
* \author Steve Murphy <murf@digium.com>
*
* \arg See also \ref AstCEL
*
* Logs in LOG_DIR/cel_custom
* \ingroup cel_drivers
*/
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/paths.h"
#include "asterisk/channel.h"
#include "asterisk/cel.h"
#include "asterisk/module.h"
#include "asterisk/config.h"
#include "asterisk/pbx.h"
#include "asterisk/utils.h"
#include "asterisk/lock.h"
#include "asterisk/threadstorage.h"
#include "asterisk/strings.h"
#define CUSTOM_LOG_DIR "/cel_custom"
#define CONFIG "cel_custom.conf"
AST_THREADSTORAGE(custom_buf);
static const char name[] = "cel-custom";
struct cel_config {
AST_DECLARE_STRING_FIELDS(
AST_STRING_FIELD(filename);
AST_STRING_FIELD(format);
);
ast_mutex_t lock;
AST_RWLIST_ENTRY(cel_config) list;
};
static struct ast_event_sub *event_sub = NULL;
static AST_RWLIST_HEAD_STATIC(sinks, cel_config);
static void free_config(void)
{
struct cel_config *sink;
while ((sink = AST_RWLIST_REMOVE_HEAD(&sinks, list))) {
ast_mutex_destroy(&sink->lock);
ast_free(sink);
}
}
static int load_config(void)
{
struct ast_config *cfg;
struct ast_variable *var;
struct ast_flags config_flags = { 0 };
int res = 0;
cfg = ast_config_load(CONFIG, config_flags);
if (!cfg || cfg == CONFIG_STATUS_FILEINVALID) {
ast_log(LOG_ERROR, "Unable to load " CONFIG ". Not logging CEL to custom CSVs.\n");
return -1;
}
var = ast_variable_browse(cfg, "mappings");
while (var) {
if (!ast_strlen_zero(var->name) && !ast_strlen_zero(var->value)) {
struct cel_config *sink = ast_calloc_with_stringfields(1, struct cel_config, 1024);
if (!sink) {
ast_log(LOG_ERROR, "Unable to allocate memory for configuration settings.\n");
res = -2;
break;
}
ast_string_field_build(sink, format, "%s\n", var->value);
ast_string_field_build(sink, filename, "%s/%s/%s", ast_config_AST_LOG_DIR, name, var->name);
ast_mutex_init(&sink->lock);
AST_RWLIST_INSERT_TAIL(&sinks, sink, list);
} else {
ast_log(LOG_NOTICE, "Mapping must have both a filename and a format at line %d\n", var->lineno);
}
var = var->next;
}
ast_config_destroy(cfg);
return res;
}
static void custom_log(const struct ast_event *event, void *userdata)
{
struct ast_channel *dummy;
struct ast_str *str;
struct cel_config *config;
/* Batching saves memory management here. Otherwise, it's the same as doing an allocation and free each time. */
if (!(str = ast_str_thread_get(&custom_buf, 16))) {
return;
}
dummy = ast_cel_fabricate_channel_from_event(event);
if (!dummy) {
ast_log(LOG_ERROR, "Unable to fabricate channel from CEL event.\n");
return;
}
AST_RWLIST_RDLOCK(&sinks);
AST_LIST_TRAVERSE(&sinks, config, list) {
FILE *out;
ast_str_substitute_variables(&str, 0, dummy, config->format);
/* Even though we have a lock on the list, we could be being chased by
another thread and this lock ensures that we won't step on anyone's
toes. Once each CEL backend gets it's own thread, this lock can be
removed. */
ast_mutex_lock(&config->lock);
/* Because of the absolutely unconditional need for the
highest reliability possible in writing billing records,
we open write and close the log file each time */
if ((out = fopen(config->filename, "a"))) {
fputs(ast_str_buffer(str), out);
fflush(out); /* be particularly anal here */
fclose(out);
} else {
ast_log(LOG_ERROR, "Unable to re-open master file %s : %s\n", config->filename, strerror(errno));
}
ast_mutex_unlock(&config->lock);
}
AST_RWLIST_UNLOCK(&sinks);
ast_channel_release(dummy);
}
static int unload_module(void)
{
if (event_sub) {
event_sub = ast_event_unsubscribe(event_sub);
}
if (AST_RWLIST_WRLOCK(&sinks)) {
event_sub = ast_event_subscribe(AST_EVENT_CEL, custom_log, "CEL Custom CSV Logging",
NULL, AST_EVENT_IE_END);
ast_log(LOG_ERROR, "Unable to lock sink list. Unload failed.\n");
return -1;
}
free_config();
AST_RWLIST_UNLOCK(&sinks);
return 0;
}
static enum ast_module_load_result load_module(void)
{
if (AST_RWLIST_WRLOCK(&sinks)) {
ast_log(LOG_ERROR, "Unable to lock sink list. Load failed.\n");
return AST_MODULE_LOAD_FAILURE;
}
load_config();
AST_RWLIST_UNLOCK(&sinks);
event_sub = ast_event_subscribe(AST_EVENT_CEL, custom_log, "CEL Custom CSV Logging",
NULL, AST_EVENT_IE_END);
return AST_MODULE_LOAD_SUCCESS;
}
static int reload(void)
{
if (AST_RWLIST_WRLOCK(&sinks)) {
ast_log(LOG_ERROR, "Unable to lock sink list. Load failed.\n");
return AST_MODULE_LOAD_FAILURE;
}
free_config();
load_config();
AST_RWLIST_UNLOCK(&sinks);
return AST_MODULE_LOAD_SUCCESS;
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Customizable Comma Separated Values CEL Backend",
.load = load_module,
.unload = unload_module,
.reload = reload,
);

175
cel/cel_manager.c Normal file
View File

@ -0,0 +1,175 @@
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 2008 - 2009, Digium, Inc.
*
* Steve Murphy <murf@digium.com>
* who freely borrowed code from the cdr equivalents
* (see cdr/cdr_manager.c)
*
* 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 Asterisk Channel Event records.
*
* See also
* \arg \ref AstCDR
* \arg \ref AstAMI
* \arg \ref Config_ami
* \ingroup cel_drivers
*/
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/channel.h"
#include "asterisk/cel.h"
#include "asterisk/module.h"
#include "asterisk/logger.h"
#include "asterisk/utils.h"
#include "asterisk/manager.h"
#include "asterisk/config.h"
static const char DATE_FORMAT[] = "%Y-%m-%d %T";
static const char CONF_FILE[] = "cel.conf";
static int enablecel;
static struct ast_event_sub *event_sub;
static void manager_log(const struct ast_event *event, void *userdata)
{
struct ast_tm timeresult;
char start_time[80] = "";
struct ast_cel_event_record record = {
.version = AST_CEL_EVENT_RECORD_VERSION,
};
if (ast_cel_fill_record(event, &record)) {
return;
}
if (!enablecel) {
return;
}
ast_localtime(&record.event_time, &timeresult, NULL);
ast_strftime(start_time, sizeof(start_time), DATE_FORMAT, &timeresult);
manager_event(EVENT_FLAG_CALL, "CEL",
"EventName: %s\r\n"
"AccountCode: %s\r\n"
"CallerIDnum: %s\r\n"
"CallerIDname: %s\r\n"
"CallerIDani: %s\r\n"
"CallerIDrdnis: %s\r\n"
"CallerIDdnid: %s\r\n"
"Exten: %s\r\n"
"Context: %s\r\n"
"Channel: %s\r\n"
"Application: %s\r\n"
"AppData: %s\r\n"
"EventTime: %s\r\n"
"AMAFlags: %s\r\n"
"UniqueID: %s\r\n"
"LinkedID: %s\r\n"
"Userfield: %s\r\n"
"Peer: %s\r\n",
record.event_name, record.account_code, record.caller_id_num,
record.caller_id_name, record.caller_id_ani, record.caller_id_rdnis,
record.caller_id_dnid, record.extension, record.context, record.channel_name,
record.application_name, record.application_data, start_time,
ast_cel_get_ama_flag_name(record.amaflag), record.unique_id, record.linked_id,
record.user_field, record.peer);
}
static int load_config(int reload)
{
const char *cat = NULL;
struct ast_config *cfg;
struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
struct ast_variable *v;
int newenablecel = 0;
cfg = ast_config_load(CONF_FILE, config_flags);
if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
return 0;
}
if (!cfg) {
ast_log(LOG_WARNING, "Failed to load configuration file. CEL manager Module not activated.\n");
enablecel = 0;
return -1;
}
while ((cat = ast_category_browse(cfg, cat))) {
if (strcasecmp(cat, "manager")) {
continue;
}
for (v = ast_variable_browse(cfg, cat); v; v = v->next) {
if (!strcasecmp(v->name, "enabled")) {
newenablecel = ast_true(v->value);
} else {
ast_log(LOG_NOTICE, "Unknown option '%s' specified "
"for cel_manager.\n", v->name);
}
}
}
ast_config_destroy(cfg);
if (enablecel && !newenablecel) {
if (event_sub) {
event_sub = ast_event_unsubscribe(event_sub);
}
} else if (!enablecel && newenablecel) {
event_sub = ast_event_subscribe(AST_EVENT_CEL, manager_log, "Manager Event Logging", NULL, AST_EVENT_IE_END);
if (!event_sub) {
ast_log(LOG_ERROR, "Unable to register Asterisk Call Manager CEL handling\n");
}
}
enablecel = newenablecel;
return 0;
}
static int unload_module(void)
{
if (event_sub) {
event_sub = ast_event_unsubscribe(event_sub);
}
return 0;
}
static int load_module(void)
{
if (load_config(0)) {
return AST_MODULE_LOAD_DECLINE;
}
return AST_MODULE_LOAD_SUCCESS;
}
static int reload(void)
{
return load_config(1);
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Asterisk Manager Interface CEL Backend",
.load = load_module,
.unload = unload_module,
.reload = reload,
);

565
cel/cel_pgsql.c Normal file
View File

@ -0,0 +1,565 @@
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 2008
*
* Steve Murphy - adapted to CEL, from:
* Matthew D. Hardeman <mhardemn@papersoft.com>
* Adapted from the MySQL CDR logger originally by James Sharp
*
* Modified April, 2007; Dec, 2008
* Steve Murphy <murf@digium.com>
* Modified September 2003
* Matthew D. Hardeman <mhardemn@papersoft.com>
*
* 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 PostgreSQL CEL logger
*
* \author Steve Murphy <murf@digium.com>
* \extref PostgreSQL http://www.postgresql.org/
*
* See also
* \arg \ref Config_cel
* \arg http://www.postgresql.org/
* \ingroup cel_drivers
*/
/*** MODULEINFO
<depend>pgsql</depend>
***/
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include <libpq-fe.h>
#include "asterisk/config.h"
#include "asterisk/options.h"
#include "asterisk/channel.h"
#include "asterisk/cel.h"
#include "asterisk/module.h"
#include "asterisk/logger.h"
#include "asterisk.h"
#define DATE_FORMAT "%Y-%m-%d %T"
static char *config = "cel_pgsql.conf";
static char *pghostname = NULL, *pgdbname = NULL, *pgdbuser = NULL, *pgpassword = NULL, *pgdbport = NULL, *table = NULL;
static int connected = 0;
static int maxsize = 512, maxsize2 = 512;
AST_MUTEX_DEFINE_STATIC(pgsql_lock);
static PGconn *conn = NULL;
static PGresult *result = NULL;
static struct ast_event_sub *event_sub = NULL;
struct columns {
char *name;
char *type;
int len;
unsigned int notnull:1;
unsigned int hasdefault:1;
AST_RWLIST_ENTRY(columns) list;
};
static AST_RWLIST_HEAD_STATIC(psql_columns, columns);
#define LENGTHEN_BUF1(size) \
do { \
/* Lengthen buffer, if necessary */ \
if (ast_str_strlen(sql) + size + 1 > ast_str_size(sql)) { \
if (ast_str_make_space(&sql, ((ast_str_size(sql) + size + 3) / 512 + 1) * 512) != 0) { \
ast_log(LOG_ERROR, "Unable to allocate sufficient memory. Insert CDR failed.\n"); \
ast_free(sql); \
ast_free(sql2); \
AST_RWLIST_UNLOCK(&psql_columns); \
return; \
} \
} \
} while (0)
#define LENGTHEN_BUF2(size) \
do { \
if (ast_str_strlen(sql2) + size + 1 > ast_str_size(sql2)) { \
if (ast_str_make_space(&sql2, ((ast_str_size(sql2) + size + 3) / 512 + 1) * 512) != 0) { \
ast_log(LOG_ERROR, "Unable to allocate sufficient memory. Insert CDR failed.\n"); \
ast_free(sql); \
ast_free(sql2); \
AST_RWLIST_UNLOCK(&psql_columns); \
return; \
} \
} \
} while (0)
static void pgsql_log(const struct ast_event *event, void *userdata)
{
struct ast_tm tm;
char timestr[128];
char *pgerror;
struct ast_cel_event_record record = {
.version = AST_CEL_EVENT_RECORD_VERSION,
};
if (ast_cel_fill_record(event, &record)) {
return;
}
ast_mutex_lock(&pgsql_lock);
ast_localtime(&record.event_time, &tm, NULL);
ast_strftime(timestr, sizeof(timestr), DATE_FORMAT, &tm);
if ((!connected) && pghostname && pgdbuser && pgpassword && pgdbname) {
conn = PQsetdbLogin(pghostname, pgdbport, NULL, NULL, pgdbname, pgdbuser, pgpassword);
if (PQstatus(conn) != CONNECTION_BAD) {
connected = 1;
} else {
pgerror = PQerrorMessage(conn);
ast_log(LOG_ERROR, "cel_pgsql: Unable to connect to database server %s. Calls will not be logged!\n", pghostname);
ast_log(LOG_ERROR, "cel_pgsql: Reason: %s\n", pgerror);
PQfinish(conn);
conn = NULL;
}
}
if (connected) {
struct columns *cur;
struct ast_str *sql = ast_str_create(maxsize), *sql2 = ast_str_create(maxsize2);
char buf[257], escapebuf[513];
const char *value;
int first = 1;
if (!sql || !sql2) {
if (sql) {
ast_free(sql);
}
if (sql2) {
ast_free(sql2);
}
return;
}
ast_str_set(&sql, 0, "INSERT INTO %s (", table);
ast_str_set(&sql2, 0, " VALUES (");
#define SEP (first ? "" : ",")
AST_RWLIST_RDLOCK(&psql_columns);
AST_RWLIST_TRAVERSE(&psql_columns, cur, list) {
LENGTHEN_BUF1(strlen(cur->name) + 2);
ast_str_append(&sql, 0, "%s\"%s\"", first ? "" : ",", cur->name);
if (strcmp(cur->name, "eventtime") == 0) {
if (strncmp(cur->type, "int", 3) == 0) {
LENGTHEN_BUF2(13);
ast_str_append(&sql2, 0, "%s%ld", SEP, record.event_time.tv_sec);
} else if (strncmp(cur->type, "float", 5) == 0) {
LENGTHEN_BUF2(31);
ast_str_append(&sql2, 0, "%s%f",
SEP,
(double) record.event_time.tv_sec +
(double) record.event_time.tv_usec / 1000000.0);
} else {
/* char, hopefully */
LENGTHEN_BUF2(31);
ast_localtime(&record.event_time, &tm, NULL);
ast_strftime(buf, sizeof(buf), DATE_FORMAT, &tm);
ast_str_append(&sql2, 0, "%s'%s'", SEP, buf);
}
} else if (strcmp(cur->name, "eventtype") == 0) {
if (cur->type[0] == 'i') {
/* Get integer, no need to escape anything */
LENGTHEN_BUF2(5);
ast_str_append(&sql2, 0, "%s%d", SEP, (int) record.event_type);
} else if (strncmp(cur->type, "float", 5) == 0) {
LENGTHEN_BUF2(31);
ast_str_append(&sql2, 0, "%s%f", SEP, (double) record.event_type);
} else {
/* Char field, probably */
LENGTHEN_BUF2(strlen(record.event_name) + 1);
ast_str_append(&sql2, 0, "%s'%s'", SEP, record.event_name);
}
} else if (strcmp(cur->name, "amaflags") == 0) {
if (strncmp(cur->type, "int", 3) == 0) {
/* Integer, no need to escape anything */
LENGTHEN_BUF2(13);
ast_str_append(&sql2, 0, "%s%d", SEP, record.amaflag);
} else {
/* Although this is a char field, there are no special characters in the values for these fields */
LENGTHEN_BUF2(31);
ast_str_append(&sql2, 0, "%s'%d'", SEP, record.amaflag);
}
} else {
/* Arbitrary field, could be anything */
if (strcmp(cur->name, "userdeftype") == 0) {
value = record.user_defined_name;
} else if (strcmp(cur->name, "cid_name") == 0) {
value = record.caller_id_name;
} else if (strcmp(cur->name, "cid_num") == 0) {
value = record.caller_id_num;
} else if (strcmp(cur->name, "cid_ani") == 0) {
value = record.caller_id_ani;
} else if (strcmp(cur->name, "cid_rdnis") == 0) {
value = record.caller_id_rdnis;
} else if (strcmp(cur->name, "cid_dnid") == 0) {
value = record.caller_id_dnid;
} else if (strcmp(cur->name, "exten") == 0) {
value = record.extension;
} else if (strcmp(cur->name, "context") == 0) {
value = record.context;
} else if (strcmp(cur->name, "channame") == 0) {
value = record.channel_name;
} else if (strcmp(cur->name, "appname") == 0) {
value = record.application_name;
} else if (strcmp(cur->name, "appdata") == 0) {
value = record.application_data;
} else if (strcmp(cur->name, "accountcode") == 0) {
value = record.account_code;
} else if (strcmp(cur->name, "peeraccount") == 0) {
value = record.peer_account;
} else if (strcmp(cur->name, "uniqueid") == 0) {
value = record.unique_id;
} else if (strcmp(cur->name, "linkedid") == 0) {
value = record.linked_id;
} else if (strcmp(cur->name, "userfield") == 0) {
value = record.user_field;
} else if (strcmp(cur->name, "peer") == 0) {
value = record.peer;
} else {
value = "";
}
if (strncmp(cur->type, "int", 3) == 0) {
long long whatever;
if (value && sscanf(value, "%lld", &whatever) == 1) {
LENGTHEN_BUF2(26);
ast_str_append(&sql2, 0, "%s%lld", SEP, whatever);
} else {
LENGTHEN_BUF2(2);
ast_str_append(&sql2, 0, "%s0", SEP);
}
} else if (strncmp(cur->type, "float", 5) == 0) {
long double whatever;
if (value && sscanf(value, "%Lf", &whatever) == 1) {
LENGTHEN_BUF2(51);
ast_str_append(&sql2, 0, "%s%30Lf", SEP, whatever);
} else {
LENGTHEN_BUF2(2);
ast_str_append(&sql2, 0, "%s0", SEP);
}
/* XXX Might want to handle dates, times, and other misc fields here XXX */
} else {
if (value) {
PQescapeStringConn(conn, escapebuf, value, strlen(value), NULL);
} else {
escapebuf[0] = '\0';
}
LENGTHEN_BUF2(strlen(escapebuf) + 3);
ast_str_append(&sql2, 0, "%s'%s'", SEP, escapebuf);
}
}
first = 0;
}
AST_RWLIST_UNLOCK(&psql_columns);
LENGTHEN_BUF1(ast_str_strlen(sql2) + 2);
ast_str_append(&sql, 0, ")%s)", ast_str_buffer(sql2));
ast_verb(11, "[%s]\n", ast_str_buffer(sql));
ast_debug(2, "inserting a CEL record.\n");
/* Test to be sure we're still connected... */
/* If we're connected, and connection is working, good. */
/* Otherwise, attempt reconnect. If it fails... sorry... */
if (PQstatus(conn) == CONNECTION_OK) {
connected = 1;
} else {
ast_log(LOG_ERROR, "Connection was lost... attempting to reconnect.\n");
PQreset(conn);
if (PQstatus(conn) == CONNECTION_OK) {
ast_log(LOG_ERROR, "Connection reestablished.\n");
connected = 1;
} else {
pgerror = PQerrorMessage(conn);
ast_log(LOG_ERROR, "Unable to reconnect to database server %s. Calls will not be logged!\n", pghostname);
ast_log(LOG_ERROR, "Reason: %s\n", pgerror);
PQfinish(conn);
conn = NULL;
connected = 0;
ast_mutex_unlock(&pgsql_lock);
ast_free(sql);
ast_free(sql2);
return;
}
}
result = PQexec(conn, ast_str_buffer(sql));
if (PQresultStatus(result) != PGRES_COMMAND_OK) {
pgerror = PQresultErrorMessage(result);
ast_log(LOG_ERROR, "Failed to insert call detail record into database!\n");
ast_log(LOG_ERROR, "Reason: %s\n", pgerror);
ast_log(LOG_ERROR, "Connection may have been lost... attempting to reconnect.\n");
PQreset(conn);
if (PQstatus(conn) == CONNECTION_OK) {
ast_log(LOG_ERROR, "Connection reestablished.\n");
connected = 1;
PQclear(result);
result = PQexec(conn, ast_str_buffer(sql));
if (PQresultStatus(result) != PGRES_COMMAND_OK) {
pgerror = PQresultErrorMessage(result);
ast_log(LOG_ERROR, "HARD ERROR! Attempted reconnection failed. DROPPING CALL RECORD!\n");
ast_log(LOG_ERROR, "Reason: %s\n", pgerror);
}
}
ast_mutex_unlock(&pgsql_lock);
PQclear(result);
ast_free(sql);
ast_free(sql2);
return;
}
ast_mutex_unlock(&pgsql_lock);
}
}
static int my_unload_module(void)
{
struct columns *current;
if (event_sub) {
event_sub = ast_event_unsubscribe(event_sub);
}
if (conn) {
PQfinish(conn);
}
if (pghostname) {
ast_free(pghostname);
}
if (pgdbname) {
ast_free(pgdbname);
}
if (pgdbuser) {
ast_free(pgdbuser);
}
if (pgpassword) {
ast_free(pgpassword);
}
if (pgdbport) {
ast_free(pgdbport);
}
if (table) {
ast_free(table);
}
AST_RWLIST_WRLOCK(&psql_columns);
while ((current = AST_RWLIST_REMOVE_HEAD(&psql_columns, list))) {
ast_free(current);
}
AST_RWLIST_UNLOCK(&psql_columns);
return 0;
}
static int unload_module(void)
{
return my_unload_module();
}
static int process_my_load_module(struct ast_config *cfg)
{
struct ast_variable *var;
char *pgerror;
const char *tmp;
PGresult *result;
struct columns *cur;
if (!(var = ast_variable_browse(cfg, "global"))) {
ast_log(LOG_WARNING,"CEL pgsql config file missing global section.\n");
return AST_MODULE_LOAD_DECLINE;
}
if (!(tmp = ast_variable_retrieve(cfg,"global","hostname"))) {
ast_log(LOG_WARNING,"PostgreSQL server hostname not specified. Assuming unix socket connection\n");
tmp = ""; /* connect via UNIX-socket by default */
}
if (pghostname)
ast_free(pghostname);
if (!(pghostname = ast_strdup(tmp))) {
ast_log(LOG_WARNING,"PostgreSQL Ran out of memory copying host info\n");
return AST_MODULE_LOAD_DECLINE;
}
if (!(tmp = ast_variable_retrieve(cfg, "global", "dbname"))) {
ast_log(LOG_WARNING,"PostgreSQL database not specified. Assuming asterisk\n");
tmp = "asteriskceldb";
}
if (pgdbname)
ast_free(pgdbname);
if (!(pgdbname = ast_strdup(tmp))) {
ast_log(LOG_WARNING,"PostgreSQL Ran out of memory copying dbname info\n");
return AST_MODULE_LOAD_DECLINE;
}
if (!(tmp = ast_variable_retrieve(cfg, "global", "user"))) {
ast_log(LOG_WARNING,"PostgreSQL database user not specified. Assuming asterisk\n");
tmp = "asterisk";
}
if (pgdbuser)
ast_free(pgdbuser);
if (!(pgdbuser = ast_strdup(tmp))) {
ast_log(LOG_WARNING,"PostgreSQL Ran out of memory copying user info\n");
return AST_MODULE_LOAD_DECLINE;
}
if (!(tmp = ast_variable_retrieve(cfg, "global", "password"))) {
ast_log(LOG_WARNING, "PostgreSQL database password not specified. Assuming blank\n");
tmp = "";
}
if (pgpassword)
ast_free(pgpassword);
if (!(pgpassword = ast_strdup(tmp))) {
ast_log(LOG_WARNING,"PostgreSQL Ran out of memory copying password info\n");
return AST_MODULE_LOAD_DECLINE;
}
if (!(tmp = ast_variable_retrieve(cfg,"global","port"))) {
ast_log(LOG_WARNING,"PostgreSQL database port not specified. Using default 5432.\n");
tmp = "5432";
}
if (pgdbport)
ast_free(pgdbport);
if (!(pgdbport = ast_strdup(tmp))) {
ast_log(LOG_WARNING,"PostgreSQL Ran out of memory copying port info\n");
return AST_MODULE_LOAD_DECLINE;
}
if (!(tmp = ast_variable_retrieve(cfg, "global", "table"))) {
ast_log(LOG_WARNING,"CEL table not specified. Assuming cel\n");
tmp = "cel";
}
if (table)
ast_free(table);
if (!(table = ast_strdup(tmp))) {
return AST_MODULE_LOAD_DECLINE;
}
if (option_debug) {
if (ast_strlen_zero(pghostname)) {
ast_debug(3, "cel_pgsql: using default unix socket\n");
} else {
ast_debug(3, "cel_pgsql: got hostname of %s\n", pghostname);
}
ast_debug(3, "cel_pgsql: got port of %s\n", pgdbport);
ast_debug(3, "cel_pgsql: got user of %s\n", pgdbuser);
ast_debug(3, "cel_pgsql: got dbname of %s\n", pgdbname);
ast_debug(3, "cel_pgsql: got password of %s\n", pgpassword);
ast_debug(3, "cel_pgsql: got sql table name of %s\n", table);
}
conn = PQsetdbLogin(pghostname, pgdbport, NULL, NULL, pgdbname, pgdbuser, pgpassword);
if (PQstatus(conn) != CONNECTION_BAD) {
char sqlcmd[512];
char *fname, *ftype, *flen, *fnotnull, *fdef;
char *tableptr;
int i, rows;
ast_debug(1, "Successfully connected to PostgreSQL database.\n");
connected = 1;
/* Remove any schema name from the table */
if ((tableptr = strrchr(table, '.'))) {
tableptr++;
} else {
tableptr = table;
}
/* Query the columns */
snprintf(sqlcmd, sizeof(sqlcmd), "select a.attname, t.typname, a.attlen, a.attnotnull, d.adsrc from pg_class c, pg_type t, pg_attribute a left outer join pg_attrdef d on a.atthasdef and d.adrelid = a.attrelid and d.adnum = a.attnum where c.oid = a.attrelid and a.atttypid = t.oid and (a.attnum > 0) and c.relname = '%s' order by c.relname, attnum", tableptr);
result = PQexec(conn, sqlcmd);
if (PQresultStatus(result) != PGRES_TUPLES_OK) {
pgerror = PQresultErrorMessage(result);
ast_log(LOG_ERROR, "Failed to query database columns: %s\n", pgerror);
PQclear(result);
unload_module();
return AST_MODULE_LOAD_DECLINE;
}
rows = PQntuples(result);
for (i = 0; i < rows; i++) {
fname = PQgetvalue(result, i, 0);
ftype = PQgetvalue(result, i, 1);
flen = PQgetvalue(result, i, 2);
fnotnull = PQgetvalue(result, i, 3);
fdef = PQgetvalue(result, i, 4);
ast_verb(4, "Found column '%s' of type '%s'\n", fname, ftype);
cur = ast_calloc(1, sizeof(*cur) + strlen(fname) + strlen(ftype) + 2);
if (cur) {
sscanf(flen, "%d", &cur->len);
cur->name = (char *)cur + sizeof(*cur);
cur->type = (char *)cur + sizeof(*cur) + strlen(fname) + 1;
strcpy(cur->name, fname);
strcpy(cur->type, ftype);
if (*fnotnull == 't') {
cur->notnull = 1;
} else {
cur->notnull = 0;
}
if (!ast_strlen_zero(fdef)) {
cur->hasdefault = 1;
} else {
cur->hasdefault = 0;
}
AST_RWLIST_INSERT_TAIL(&psql_columns, cur, list);
}
}
PQclear(result);
} else {
pgerror = PQerrorMessage(conn);
ast_log(LOG_ERROR, "cel_pgsql: Unable to connect to database server %s. CALLS WILL NOT BE LOGGED!!\n", pghostname);
ast_log(LOG_ERROR, "cel_pgsql: Reason: %s\n", pgerror);
connected = 0;
}
return AST_MODULE_LOAD_SUCCESS;
}
static int my_load_module(int reload)
{
struct ast_config *cfg;
int res;
struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
if ((cfg = ast_config_load(config, config_flags)) == NULL || cfg == CONFIG_STATUS_FILEINVALID) {
ast_log(LOG_WARNING, "Unable to load config for PostgreSQL CEL's: %s\n", config);
return AST_MODULE_LOAD_DECLINE;
} else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
return AST_MODULE_LOAD_SUCCESS;
}
res = process_my_load_module(cfg);
ast_config_destroy(cfg);
event_sub = ast_event_subscribe(AST_EVENT_CEL, pgsql_log, "CEL PGSQL backend", NULL, AST_EVENT_IE_END);
if (!event_sub) {
ast_log(LOG_WARNING, "Unable to subscribe to CEL events for pgsql\n");
return AST_MODULE_LOAD_DECLINE;
}
return AST_MODULE_LOAD_SUCCESS;
}
static int load_module(void)
{
return my_load_module(0);
}
static int reload(void)
{
my_unload_module();
return my_load_module(1);
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "PostgreSQL CEL Backend",
.load = load_module,
.unload = unload_module,
.reload = reload,
);

254
cel/cel_radius.c Normal file
View File

@ -0,0 +1,254 @@
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 1999 - 2005, Digium, Inc.
*
* Mark Spencer <markster@digium.com>
*
* 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 RADIUS CEL Support
* \author Philippe Sultan
* \extref The Radius Client Library - http://developer.berlios.de/projects/radiusclient-ng/
*
* \arg See also \ref AstCEL
* \ingroup cel_drivers
*/
/*** MODULEINFO
<depend>radius</depend>
***/
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Rev$")
#include <radiusclient-ng.h>
#include "asterisk/channel.h"
#include "asterisk/cel.h"
#include "asterisk/module.h"
#include "asterisk/logger.h"
#include "asterisk/utils.h"
#include "asterisk/options.h"
/*! ISO 8601 standard format */
#define DATE_FORMAT "%Y-%m-%d %T %z"
#define VENDOR_CODE 22736
enum {
PW_AST_ACCT_CODE = 101,
PW_AST_CIDNUM = 102,
PW_AST_CIDNAME = 103,
PW_AST_CIDANI = 104,
PW_AST_CIDRDNIS = 105,
PW_AST_CIDDNID = 106,
PW_AST_EXTEN = 107,
PW_AST_CONTEXT = 108,
PW_AST_CHANNAME = 109,
PW_AST_APPNAME = 110,
PW_AST_APPDATA = 111,
PW_AST_EVENT_TIME = 112,
PW_AST_AMA_FLAGS = 113,
PW_AST_UNIQUE_ID = 114,
PW_AST_USER_NAME = 115,
PW_AST_LINKED_ID = 116,
};
enum {
/*! Log dates and times in UTC */
RADIUS_FLAG_USEGMTIME = (1 << 0),
/*! Log Unique ID */
RADIUS_FLAG_LOGUNIQUEID = (1 << 1),
/*! Log User Field */
RADIUS_FLAG_LOGUSERFIELD = (1 << 2)
};
static char *cel_config = "cel.conf";
static char radiuscfg[PATH_MAX] = "/etc/radiusclient-ng/radiusclient.conf";
static struct ast_flags global_flags = { RADIUS_FLAG_USEGMTIME | RADIUS_FLAG_LOGUNIQUEID | RADIUS_FLAG_LOGUSERFIELD };
static rc_handle *rh = NULL;
static struct ast_event_sub *event_sub = NULL;
#define ADD_VENDOR_CODE(x,y) (rc_avpair_add(rh, send, x, &y, strlen(y), VENDOR_CODE))
static int build_radius_record(VALUE_PAIR **send, struct ast_cel_event_record *record)
{
int recordtype = PW_STATUS_STOP;
struct ast_tm tm;
char timestr[128];
char *amaflags;
if (!rc_avpair_add(rh, send, PW_ACCT_STATUS_TYPE, &recordtype, 0, 0)) {
return -1;
}
/* Account code */
if (!ADD_VENDOR_CODE(PW_AST_ACCT_CODE, record->account_code)) {
return -1;
}
/* Source */
if (!ADD_VENDOR_CODE(PW_AST_CIDNUM, record->caller_id_num)) {
return -1;
}
/* Destination */
if (!ADD_VENDOR_CODE(PW_AST_EXTEN, record->extension)) {
return -1;
}
/* Destination context */
if (!ADD_VENDOR_CODE(PW_AST_CONTEXT, record->context)) {
return -1;
}
/* Caller ID */
if (!ADD_VENDOR_CODE(PW_AST_CIDNAME, record->caller_id_name)) {
return -1;
}
/* Caller ID ani */
if (!ADD_VENDOR_CODE(PW_AST_CIDANI, record->caller_id_ani)) {
return -1;
}
/* Caller ID rdnis */
if (!ADD_VENDOR_CODE(PW_AST_CIDRDNIS, record->caller_id_rdnis)) {
return -1;
}
/* Caller ID dnid */
if (!ADD_VENDOR_CODE(PW_AST_CIDDNID, record->caller_id_dnid)) {
return -1;
}
/* Channel */
if (!ADD_VENDOR_CODE(PW_AST_CHANNAME, record->channel_name)) {
return -1;
}
/* Last Application */
if (!ADD_VENDOR_CODE(PW_AST_APPNAME, record->application_name)) {
return -1;
}
/* Last Data */
if (!ADD_VENDOR_CODE(PW_AST_APPDATA, record->application_data)) {
return -1;
}
/* Event Time */
ast_localtime(&record->event_time, &tm,
ast_test_flag(&global_flags, RADIUS_FLAG_USEGMTIME) ? "GMT" : NULL);
ast_strftime(timestr, sizeof(timestr), DATE_FORMAT, &tm);
if (!rc_avpair_add(rh, send, PW_AST_EVENT_TIME, timestr, strlen(timestr), VENDOR_CODE)) {
return -1;
}
/* AMA Flags */
amaflags = ast_strdupa(ast_cel_get_ama_flag_name(record->amaflag));
if (!rc_avpair_add(rh, send, PW_AST_AMA_FLAGS, amaflags, strlen(amaflags), VENDOR_CODE)) {
return -1;
}
if (ast_test_flag(&global_flags, RADIUS_FLAG_LOGUNIQUEID)) {
/* Unique ID */
if (!ADD_VENDOR_CODE(PW_AST_UNIQUE_ID, record->unique_id)) {
return -1;
}
}
/* LinkedID */
if (!ADD_VENDOR_CODE(PW_AST_LINKED_ID, record->linked_id)) {
return -1;
}
/* Setting Acct-Session-Id & User-Name attributes for proper generation
of Acct-Unique-Session-Id on server side */
/* Channel */
if (!rc_avpair_add(rh, send, PW_USER_NAME, &record->channel_name,
strlen(record->channel_name), 0)) {
return -1;
}
return 0;
}
static void radius_log(const struct ast_event *event, void *userdata)
{
int result = ERROR_RC;
VALUE_PAIR *send = NULL;
struct ast_cel_event_record record = {
.version = AST_CEL_EVENT_RECORD_VERSION,
};
if (ast_cel_fill_record(event, &record)) {
return;
}
if (build_radius_record(&send, &record)) {
if (option_debug) {
ast_log(LOG_DEBUG, "Unable to create RADIUS record. CEL not recorded!\n");
}
goto return_cleanup;
}
result = rc_acct(rh, 0, send);
if (result != OK_RC) {
ast_log(LOG_ERROR, "Failed to record Radius CEL record!\n");
}
return_cleanup:
if (send) {
rc_avpair_free(send);
}
}
static int unload_module(void)
{
if (event_sub) {
event_sub = ast_event_unsubscribe(event_sub);
}
return AST_MODULE_LOAD_SUCCESS;
}
static int load_module(void)
{
struct ast_config *cfg;
struct ast_flags config_flags = { 0 };
const char *tmp;
if ((cfg = ast_config_load(cel_config, config_flags))) {
ast_set2_flag(&global_flags, ast_true(ast_variable_retrieve(cfg, "radius", "usegmtime")), RADIUS_FLAG_USEGMTIME);
if ((tmp = ast_variable_retrieve(cfg, "radius", "radiuscfg"))) {
ast_copy_string(radiuscfg, tmp, sizeof(radiuscfg));
}
ast_config_destroy(cfg);
} else {
return AST_MODULE_LOAD_DECLINE;
}
/* start logging */
rc_openlog("asterisk");
/* read radiusclient-ng config file */
if (!(rh = rc_read_config(radiuscfg))) {
ast_log(LOG_NOTICE, "Cannot load radiusclient-ng configuration file %s.\n", radiuscfg);
return AST_MODULE_LOAD_DECLINE;
}
/* read radiusclient-ng dictionaries */
if (rc_read_dictionary(rh, rc_conf_str(rh, "dictionary"))) {
ast_log(LOG_NOTICE, "Cannot load radiusclient-ng dictionary file.\n");
return AST_MODULE_LOAD_DECLINE;
}
event_sub = ast_event_subscribe(AST_EVENT_CEL, radius_log, "CEL Radius Logging", NULL, AST_EVENT_IE_END);
if (!event_sub) {
return AST_MODULE_LOAD_DECLINE;
} else {
return AST_MODULE_LOAD_SUCCESS;
}
}
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "RADIUS CEL Backend");

364
cel/cel_sqlite3_custom.c Normal file
View File

@ -0,0 +1,364 @@
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 2007, Digium, Inc.
*
* Steve Murphy <murf@digium.com> borrowed code from cdr,
* Mark Spencer <markster@digium.com> and others.
*
* 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 Custom SQLite3 CEL records.
*
* \author Adapted by Steve Murphy <murf@digium.com> from
* Alejandro Rios <alejandro.rios@avatar.com.co> and
* Russell Bryant <russell@digium.com> from
* cdr_mysql_custom by Edward Eastman <ed@dm3.co.uk>,
* and cdr_sqlite by Holger Schurig <hs4233@mail.mn-solutions.de>
*
*
* \arg See also \ref AstCEL
*
*
* \ingroup cel_drivers
*/
/*** MODULEINFO
<depend>sqlite3</depend>
***/
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include <sqlite3.h>
#include "asterisk/paths.h"
#include "asterisk/channel.h"
#include "asterisk/cel.h"
#include "asterisk/module.h"
#include "asterisk/config.h"
#include "asterisk/pbx.h"
#include "asterisk/logger.h"
#include "asterisk/utils.h"
#include "asterisk/cli.h"
#include "asterisk/options.h"
#include "asterisk/stringfields.h"
AST_MUTEX_DEFINE_STATIC(lock);
static const char config_file[] = "cel_sqlite3_custom.conf";
static const char name[] = "cel_sqlite3_custom";
static sqlite3 *db = NULL;
static char table[80];
/*! XXX \bug Handling of this var is crash prone on reloads */
static char *columns;
static struct ast_event_sub *event_sub = NULL;
struct values {
char *expression;
AST_LIST_ENTRY(values) list;
};
static AST_LIST_HEAD_STATIC(sql_values, values);
static void free_config(void);
static int load_column_config(const char *tmp)
{
char *col = NULL;
char *cols = NULL, *save = NULL;
char *escaped = NULL;
struct ast_str *column_string = NULL;
if (ast_strlen_zero(tmp)) {
ast_log(LOG_WARNING, "Column names not specified. Module not loaded.\n");
return -1;
}
if (!(column_string = ast_str_create(1024))) {
ast_log(LOG_ERROR, "Out of memory creating temporary buffer for column list for table '%s.'\n", table);
return -1;
}
if (!(save = cols = ast_strdup(tmp))) {
ast_log(LOG_ERROR, "Out of memory creating temporary buffer for column list for table '%s.'\n", table);
ast_free(column_string);
return -1;
}
while ((col = strsep(&cols, ","))) {
col = ast_strip(col);
escaped = sqlite3_mprintf("%q", col);
if (!escaped) {
ast_log(LOG_ERROR, "Out of memory creating entry for column '%s' in table '%s.'\n", col, table);
ast_free(column_string);
ast_free(save);
return -1;
}
ast_str_append(&column_string, 0, "%s%s", ast_str_strlen(column_string) ? "," : "", escaped);
sqlite3_free(escaped);
}
if (!(columns = ast_strdup(ast_str_buffer(column_string)))) {
ast_log(LOG_ERROR, "Out of memory copying columns string for table '%s.'\n", table);
ast_free(column_string);
ast_free(save);
return -1;
}
ast_free(column_string);
ast_free(save);
return 0;
}
static int load_values_config(const char *tmp)
{
char *val = NULL;
char *vals = NULL, *save = NULL;
struct values *value = NULL;
if (ast_strlen_zero(tmp)) {
ast_log(LOG_WARNING, "Values not specified. Module not loaded.\n");
return -1;
}
if (!(save = vals = ast_strdup(tmp))) {
ast_log(LOG_ERROR, "Out of memory creating temporary buffer for value '%s'\n", tmp);
return -1;
}
while ((val = strsep(&vals, ","))) {
/* Strip the single quotes off if they are there */
val = ast_strip_quoted(val, "'", "'");
value = ast_calloc(sizeof(char), sizeof(*value) + strlen(val) + 1);
if (!value) {
ast_log(LOG_ERROR, "Out of memory creating entry for value '%s'\n", val);
ast_free(save);
return -1;
}
value->expression = (char *) value + sizeof(*value);
ast_copy_string(value->expression, val, strlen(val) + 1);
AST_LIST_INSERT_TAIL(&sql_values, value, list);
}
ast_free(save);
return 0;
}
static int load_config(int reload)
{
struct ast_config *cfg;
struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
struct ast_variable *mappingvar;
const char *tmp;
if ((cfg = ast_config_load(config_file, config_flags)) == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEINVALID) {
ast_log(LOG_WARNING, "Failed to %sload configuration file. %s\n",
reload ? "re" : "", reload ? "" : "Module not activated.");
return -1;
} else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
return 0;
}
if (reload) {
free_config();
}
if (!(mappingvar = ast_variable_browse(cfg, "master"))) {
/* Nothing configured */
ast_config_destroy(cfg);
return -1;
}
/* Mapping must have a table name */
if (!ast_strlen_zero(tmp = ast_variable_retrieve(cfg, "master", "table"))) {
ast_copy_string(table, tmp, sizeof(table));
} else {
ast_log(LOG_WARNING, "Table name not specified. Assuming cel.\n");
strcpy(table, "cel");
}
/* Columns */
if (load_column_config(ast_variable_retrieve(cfg, "master", "columns"))) {
ast_config_destroy(cfg);
free_config();
return -1;
}
/* Values */
if (load_values_config(ast_variable_retrieve(cfg, "master", "values"))) {
ast_config_destroy(cfg);
free_config();
return -1;
}
ast_verb(3, "Logging CEL records to table '%s' in 'master.db'\n", table);
ast_config_destroy(cfg);
return 0;
}
static void free_config(void)
{
struct values *value;
if (db) {
sqlite3_close(db);
db = NULL;
}
if (columns) {
ast_free(columns);
columns = NULL;
}
while ((value = AST_LIST_REMOVE_HEAD(&sql_values, list))) {
ast_free(value);
}
}
static void sqlite3_log(const struct ast_event *event, void *userdata)
{
char *error = NULL;
char *sql = NULL;
int count = 0;
if (db == NULL) {
/* Should not have loaded, but be failsafe. */
return;
}
ast_mutex_lock(&lock);
{ /* Make it obvious that only sql should be used outside of this block */
char *escaped;
char subst_buf[2048];
struct values *value;
struct ast_channel *dummy;
struct ast_str *value_string = ast_str_create(1024);
dummy = ast_cel_fabricate_channel_from_event(event);
if (!dummy) {
ast_log(LOG_ERROR, "Unable to fabricate channel from CEL event.\n");
ast_free(value_string);
ast_mutex_unlock(&lock);
return;
}
AST_LIST_TRAVERSE(&sql_values, value, list) {
pbx_substitute_variables_helper(dummy, value->expression, subst_buf, sizeof(subst_buf) - 1);
escaped = sqlite3_mprintf("%q", subst_buf);
ast_str_append(&value_string, 0, "%s'%s'", ast_str_strlen(value_string) ? "," : "", escaped);
sqlite3_free(escaped);
}
sql = sqlite3_mprintf("INSERT INTO %q (%s) VALUES (%s)", table, columns, ast_str_buffer(value_string));
ast_debug(1, "About to log: %s\n", sql);
dummy = ast_channel_release(dummy);
ast_free(value_string);
}
/* XXX This seems awful arbitrary... */
for (count = 0; count < 5; count++) {
int res = sqlite3_exec(db, sql, NULL, NULL, &error);
if (res != SQLITE_BUSY && res != SQLITE_LOCKED) {
break;
}
usleep(200);
}
ast_mutex_unlock(&lock);
if (error) {
ast_log(LOG_ERROR, "%s. SQL: %s.\n", error, sql);
sqlite3_free(error);
}
if (sql) {
sqlite3_free(sql);
}
return;
}
static int unload_module(void)
{
if (event_sub) {
event_sub = ast_event_unsubscribe(event_sub);
}
free_config();
return 0;
}
static int load_module(void)
{
char *error;
char filename[PATH_MAX];
int res;
char *sql;
if (load_config(0)) {
return AST_MODULE_LOAD_DECLINE;
}
/* is the database there? */
snprintf(filename, sizeof(filename), "%s/master.db", ast_config_AST_LOG_DIR);
res = sqlite3_open(filename, &db);
if (res != SQLITE_OK) {
ast_log(LOG_ERROR, "Could not open database %s.\n", filename);
free_config();
return AST_MODULE_LOAD_DECLINE;
}
/* is the table there? */
sql = sqlite3_mprintf("SELECT COUNT(AcctId) FROM %q;", table);
res = sqlite3_exec(db, sql, NULL, NULL, NULL);
sqlite3_free(sql);
if (res != SQLITE_OK) {
/* We don't use %q for the column list here since we already escaped when building it */
sql = sqlite3_mprintf("CREATE TABLE %q (AcctId INTEGER PRIMARY KEY, %s)", table, columns);
res = sqlite3_exec(db, sql, NULL, NULL, &error);
sqlite3_free(sql);
if (res != SQLITE_OK) {
ast_log(LOG_WARNING, "Unable to create table '%s': %s.\n", table, error);
sqlite3_free(error);
free_config();
return AST_MODULE_LOAD_DECLINE;
}
}
event_sub = ast_event_subscribe(AST_EVENT_CEL, sqlite3_log, "CEL sqlite3 custom backend", NULL, AST_EVENT_IE_END);
if (!event_sub) {
ast_log(LOG_ERROR, "Unable to register custom SQLite3 CEL handling\n");
free_config();
return AST_MODULE_LOAD_DECLINE;
}
return AST_MODULE_LOAD_SUCCESS;
}
static int reload(void)
{
int res = 0;
ast_mutex_lock(&lock);
res = load_config(1);
ast_mutex_lock(&lock);
return res;
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "SQLite3 Custom CEL Module",
.load = load_module,
.unload = unload_module,
.reload = reload,
);

587
cel/cel_tds.c Normal file
View File

@ -0,0 +1,587 @@
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 2008, 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 FreeTDS CEL logger
*
* See also
* \arg \ref Config_cdr
* \arg http://www.freetds.org/
* \ingroup cdr_drivers
*/
/*! \verbatim
*
* Table Structure for `cdr`
*
CREATE TABLE [dbo].[cdr] (
[accountcode] [varchar] (20) NULL ,
[cidname] [varchar] (80) NULL ,
[cidnum] [varchar] (80) NULL ,
[cidani] [varchar] (80) NULL ,
[cidrdnis] [varchar] (80) NULL ,
[ciddnid] [varchar] (80) NULL ,
[exten] [varchar] (80) NULL ,
[context] [varchar] (80) NULL ,
[channame] [varchar] (80) NULL ,
[appname] [varchar] (80) NULL ,
[appdata] [varchar] (80) NULL ,
[eventtime] [datetime] NULL ,
[eventtype] [varchar] (32) NULL ,
[uniqueid] [varchar] (32) NULL ,
[linkedid] [varchar] (32) NULL ,
[amaflags] [varchar] (16) NULL ,
[userfield] [varchar] (32) NULL ,
[peer] [varchar] (32) NULL
) ON [PRIMARY]
\endverbatim
*/
/*** MODULEINFO
<depend>freetds</depend>
***/
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include <time.h>
#include <math.h>
#include "asterisk/config.h"
#include "asterisk/channel.h"
#include "asterisk/cel.h"
#include "asterisk/module.h"
#include "asterisk/logger.h"
#include <sqlfront.h>
#include <sybdb.h>
#ifdef FREETDS_PRE_0_62
#warning "You have older TDS, you should upgrade!"
#endif
#define DATE_FORMAT "%Y/%m/%d %T"
static char *config = "cel_tds.conf";
static struct ast_event_sub *event_sub = NULL;
struct cel_tds_config {
AST_DECLARE_STRING_FIELDS(
AST_STRING_FIELD(connection);
AST_STRING_FIELD(database);
AST_STRING_FIELD(username);
AST_STRING_FIELD(password);
AST_STRING_FIELD(table);
AST_STRING_FIELD(charset);
AST_STRING_FIELD(language);
);
DBPROCESS *dbproc;
unsigned int connected:1;
};
AST_MUTEX_DEFINE_STATIC(tds_lock);
static struct cel_tds_config *settings;
static char *anti_injection(const char *, int);
static void get_date(char *, size_t len, struct timeval);
static int execute_and_consume(DBPROCESS *dbproc, const char *fmt, ...)
__attribute__((format(printf, 2, 3)));
static int mssql_connect(void);
static int mssql_disconnect(void);
static void tds_log(const struct ast_event *event, void *userdata)
{
char start[80];
char *accountcode_ai, *clidnum_ai, *exten_ai, *context_ai, *clid_ai, *channel_ai, *app_ai, *appdata_ai, *uniqueid_ai, *linkedid_ai, *cidani_ai, *cidrdnis_ai, *ciddnid_ai, *peer_ai, *userfield_ai;
RETCODE erc;
int attempt = 1;
struct ast_cel_event_record record = {
.version = AST_CEL_EVENT_RECORD_VERSION,
};
if (ast_cel_fill_record(event, &record)) {
return;
}
ast_mutex_lock(&tds_lock);
accountcode_ai = anti_injection(record.account_code, 20);
clidnum_ai = anti_injection(record.caller_id_num, 80);
clid_ai = anti_injection(record.caller_id_name, 80);
cidani_ai = anti_injection(record.caller_id_ani, 80);
cidrdnis_ai = anti_injection(record.caller_id_rdnis, 80);
ciddnid_ai = anti_injection(record.caller_id_dnid, 80);
exten_ai = anti_injection(record.extension, 80);
context_ai = anti_injection(record.context, 80);
channel_ai = anti_injection(record.channel_name, 80);
app_ai = anti_injection(record.application_name, 80);
appdata_ai = anti_injection(record.application_data, 80);
uniqueid_ai = anti_injection(record.unique_id, 32);
linkedid_ai = anti_injection(record.linked_id, 32);
userfield_ai = anti_injection(record.user_field, 32);
peer_ai = anti_injection(record.peer, 32);
get_date(start, sizeof(start), record.event_time);
retry:
/* Ensure that we are connected */
if (!settings->connected) {
ast_log(LOG_NOTICE, "Attempting to reconnect to %s (Attempt %d)\n", settings->connection, attempt);
if (mssql_connect()) {
/* Connect failed */
if (attempt++ < 3) {
goto retry;
}
goto done;
}
}
erc = dbfcmd(settings->dbproc,
"INSERT INTO %s "
"("
"accountcode,"
"cidnum,"
"cidname,"
"cidani,"
"cidrdnis,"
"ciddnid,"
"exten,"
"context,"
"channel,"
"appname,"
"appdata,"
"eventtime,"
"eventtype,"
"amaflags, "
"uniqueid,"
"linkedid,"
"userfield,"
"peer"
") "
"VALUES "
"("
"'%s'," /* accountcode */
"'%s'," /* clidnum */
"'%s'," /* clid */
"'%s'," /* cid-ani */
"'%s'," /* cid-rdnis */
"'%s'," /* cid-dnid */
"'%s'," /* exten */
"'%s'," /* context */
"'%s'," /* channel */
"'%s'," /* app */
"'%s'," /* appdata */
"%s, " /* eventtime */
"'%s'," /* eventtype */
"'%s'," /* amaflags */
"'%s'," /* uniqueid */
"'%s'," /* linkedid */
"'%s'," /* userfield */
"'%s'" /* peer */
")",
settings->table, accountcode_ai, clidnum_ai, clid_ai, cidani_ai, cidrdnis_ai,
ciddnid_ai, exten_ai, context_ai, channel_ai, app_ai, appdata_ai, start,
record.event_name, ast_cel_get_ama_flag_name(record.amaflag), uniqueid_ai, linkedid_ai,
userfield_ai, peer_ai);
if (erc == FAIL) {
if (attempt++ < 3) {
ast_log(LOG_NOTICE, "Failed to build INSERT statement, retrying...\n");
mssql_disconnect();
goto retry;
} else {
ast_log(LOG_ERROR, "Failed to build INSERT statement, no CEL was logged.\n");
goto done;
}
}
if (dbsqlexec(settings->dbproc) == FAIL) {
if (attempt++ < 3) {
ast_log(LOG_NOTICE, "Failed to execute INSERT statement, retrying...\n");
mssql_disconnect();
goto retry;
} else {
ast_log(LOG_ERROR, "Failed to execute INSERT statement, no CEL was logged.\n");
goto done;
}
}
/* Consume any results we might get back (this is more of a sanity check than
* anything else, since an INSERT shouldn't return results). */
while (dbresults(settings->dbproc) != NO_MORE_RESULTS) {
while (dbnextrow(settings->dbproc) != NO_MORE_ROWS);
}
done:
ast_mutex_unlock(&tds_lock);
free(accountcode_ai);
free(clidnum_ai);
free(clid_ai);
free(cidani_ai);
free(cidrdnis_ai);
free(ciddnid_ai);
free(exten_ai);
free(context_ai);
free(channel_ai);
free(app_ai);
free(appdata_ai);
free(uniqueid_ai);
free(linkedid_ai);
free(userfield_ai);
free(peer_ai);
return;
}
static char *anti_injection(const char *str, int len)
{
/* Reference to http://www.nextgenss.com/papers/advanced_sql_injection.pdf */
char *buf;
char *buf_ptr, *srh_ptr;
char *known_bad[] = {"select", "insert", "update", "delete", "drop", ";", "--", "\0"};
int idx;
if (!(buf = ast_calloc(1, len + 1))) {
ast_log(LOG_ERROR, "Out of memory\n");
return NULL;
}
buf_ptr = buf;
/* Escape single quotes */
for (; *str && strlen(buf) < len; str++) {
if (*str == '\'') {
*buf_ptr++ = '\'';
}
*buf_ptr++ = *str;
}
*buf_ptr = '\0';
/* Erase known bad input */
for (idx = 0; *known_bad[idx]; idx++) {
while ((srh_ptr = strcasestr(buf, known_bad[idx]))) {
memmove(srh_ptr, srh_ptr + strlen(known_bad[idx]), strlen(srh_ptr + strlen(known_bad[idx])) + 1);
}
}
return buf;
}
static void get_date(char *dateField, size_t len, struct timeval when)
{
/* To make sure we have date variable if not insert null to SQL */
if (!ast_tvzero(when)) {
struct ast_tm tm;
ast_localtime(&when, &tm, NULL);
ast_strftime(dateField, len, "'" DATE_FORMAT "'", &tm);
} else {
ast_copy_string(dateField, "null", len);
}
}
static int execute_and_consume(DBPROCESS *dbproc, const char *fmt, ...)
{
va_list ap;
char *buffer;
va_start(ap, fmt);
if (ast_vasprintf(&buffer, fmt, ap) < 0) {
va_end(ap);
return 1;
}
va_end(ap);
if (dbfcmd(dbproc, buffer) == FAIL) {
free(buffer);
return 1;
}
free(buffer);
if (dbsqlexec(dbproc) == FAIL) {
return 1;
}
/* Consume the result set (we don't really care about the result, though) */
while (dbresults(dbproc) != NO_MORE_RESULTS) {
while (dbnextrow(dbproc) != NO_MORE_ROWS);
}
return 0;
}
static int mssql_disconnect(void)
{
if (settings->dbproc) {
dbclose(settings->dbproc);
settings->dbproc = NULL;
}
settings->connected = 0;
return 0;
}
static int mssql_connect(void)
{
LOGINREC *login;
if ((login = dblogin()) == NULL) {
ast_log(LOG_ERROR, "Unable to allocate login structure for db-lib\n");
return -1;
}
DBSETLAPP(login, "TSQL");
DBSETLUSER(login, (char *) settings->username);
DBSETLPWD(login, (char *) settings->password);
if (!ast_strlen_zero(settings->charset)) {
DBSETLCHARSET(login, (char *) settings->charset);
}
if (!ast_strlen_zero(settings->language)) {
DBSETLNATLANG(login, (char *) settings->language);
}
if ((settings->dbproc = dbopen(login, (char *) settings->connection)) == NULL) {
ast_log(LOG_ERROR, "Unable to connect to %s\n", settings->connection);
dbloginfree(login);
return -1;
}
dbloginfree(login);
if (dbuse(settings->dbproc, (char *) settings->database) == FAIL) {
ast_log(LOG_ERROR, "Unable to select database %s\n", settings->database);
goto failed;
}
if (execute_and_consume(settings->dbproc, "SELECT 1 FROM [%s]", settings->table)) {
ast_log(LOG_ERROR, "Unable to find table '%s'\n", settings->table);
goto failed;
}
settings->connected = 1;
return 0;
failed:
dbclose(settings->dbproc);
settings->dbproc = NULL;
return -1;
}
static int tds_unload_module(void)
{
if (event_sub) {
event_sub = ast_event_unsubscribe(event_sub);
}
if (settings) {
ast_mutex_lock(&tds_lock);
mssql_disconnect();
ast_mutex_unlock(&tds_lock);
ast_string_field_free_memory(settings);
ast_free(settings);
}
dbexit();
return 0;
}
static int tds_error_handler(DBPROCESS *dbproc, int severity, int dberr, int oserr, char *dberrstr, char *oserrstr)
{
ast_log(LOG_ERROR, "%s (%d)\n", dberrstr, dberr);
if (oserr != DBNOERR) {
ast_log(LOG_ERROR, "%s (%d)\n", oserrstr, oserr);
}
return INT_CANCEL;
}
static int tds_message_handler(DBPROCESS *dbproc, DBINT msgno, int msgstate, int severity, char *msgtext, char *srvname, char *procname, int line)
{
ast_debug(1, "Msg %d, Level %d, State %d, Line %d\n", msgno, severity, msgstate, line);
ast_log(LOG_NOTICE, "%s\n", msgtext);
return 0;
}
static int tds_load_module(int reload)
{
struct ast_config *cfg;
const char *ptr = NULL;
struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
cfg = ast_config_load(config, config_flags);
if (!cfg || cfg == CONFIG_STATUS_FILEINVALID) {
ast_log(LOG_NOTICE, "Unable to load TDS config for CELs: %s\n", config);
return 0;
} else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
return 0;
}
if (!ast_variable_browse(cfg, "global")) {
/* nothing configured */
ast_config_destroy(cfg);
ast_log(LOG_NOTICE, "cel_tds has no global category, nothing to configure.\n");
return 0;
}
ast_mutex_lock(&tds_lock);
/* Clear out any existing settings */
ast_string_field_init(settings, 0);
ptr = ast_variable_retrieve(cfg, "global", "connection");
if (ptr) {
ast_string_field_set(settings, connection, ptr);
} else {
ast_log(LOG_ERROR, "Failed to connect: Database connection name not specified.\n");
goto failed;
}
ptr = ast_variable_retrieve(cfg, "global", "dbname");
if (ptr) {
ast_string_field_set(settings, database, ptr);
} else {
ast_log(LOG_ERROR, "Failed to connect: Database dbname not specified.\n");
goto failed;
}
ptr = ast_variable_retrieve(cfg, "global", "user");
if (ptr) {
ast_string_field_set(settings, username, ptr);
} else {
ast_log(LOG_ERROR, "Failed to connect: Database dbuser not specified.\n");
goto failed;
}
ptr = ast_variable_retrieve(cfg, "global", "password");
if (ptr) {
ast_string_field_set(settings, password, ptr);
} else {
ast_log(LOG_ERROR, "Failed to connect: Database password not specified.\n");
goto failed;
}
ptr = ast_variable_retrieve(cfg, "global", "charset");
if (ptr) {
ast_string_field_set(settings, charset, ptr);
}
ptr = ast_variable_retrieve(cfg, "global", "language");
if (ptr) {
ast_string_field_set(settings, language, ptr);
}
ptr = ast_variable_retrieve(cfg, "global", "table");
if (ptr) {
ast_string_field_set(settings, table, ptr);
} else {
ast_log(LOG_NOTICE, "Table name not specified, using 'cel' by default.\n");
ast_string_field_set(settings, table, "cel");
}
mssql_disconnect();
if (mssql_connect()) {
/* We failed to connect (mssql_connect takes care of logging it) */
goto failed;
}
ast_mutex_unlock(&tds_lock);
ast_config_destroy(cfg);
return 1;
failed:
ast_mutex_unlock(&tds_lock);
ast_config_destroy(cfg);
return 0;
}
static int reload(void)
{
return tds_load_module(1);
}
static int load_module(void)
{
if (dbinit() == FAIL) {
ast_log(LOG_ERROR, "Failed to initialize FreeTDS db-lib\n");
return AST_MODULE_LOAD_DECLINE;
}
dberrhandle(tds_error_handler);
dbmsghandle(tds_message_handler);
settings = ast_calloc(1, sizeof(*settings));
if (!settings || ast_string_field_init(settings, 256)) {
if (settings) {
ast_free(settings);
settings = NULL;
}
dbexit();
return AST_MODULE_LOAD_DECLINE;
}
if (!tds_load_module(0)) {
ast_string_field_free_memory(settings);
ast_free(settings);
settings = NULL;
dbexit();
ast_log(LOG_WARNING,"cel_tds module had config problems; declining load\n");
return AST_MODULE_LOAD_DECLINE;
}
/* Register MSSQL CEL handler */
event_sub = ast_event_subscribe(AST_EVENT_CEL, tds_log, "CEL TDS logging backend", NULL, AST_EVENT_IE_END);
if (!event_sub) {
ast_log(LOG_ERROR, "Unable to register MSSQL CEL handling\n");
ast_string_field_free_memory(settings);
ast_free(settings);
settings = NULL;
dbexit();
return AST_MODULE_LOAD_DECLINE;
}
return AST_MODULE_LOAD_SUCCESS;
}
static int unload_module(void)
{
return tds_unload_module();
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "FreeTDS CEL Backend",
.load = load_module,
.unload = unload_module,
.reload = reload,
);

View File

@ -310,7 +310,7 @@ static AST_LIST_HEAD_STATIC(agents, agent_pvt); /*!< Holds the list of agents (l
} while(0)
/*--- Forward declarations */
static struct ast_channel *agent_request(const char *type, int format, void *data, int *cause);
static struct ast_channel *agent_request(const char *type, int format, const struct ast_channel *requestor, void *data, int *cause);
static int agent_devicestate(void *data);
static int agent_digit_begin(struct ast_channel *ast, char digit);
static int agent_digit_end(struct ast_channel *ast, char digit, unsigned int duration);
@ -986,7 +986,7 @@ static struct ast_channel *agent_bridgedchannel(struct ast_channel *chan, struct
}
/*! \brief Create new agent channel */
static struct ast_channel *agent_new(struct agent_pvt *p, int state)
static struct ast_channel *agent_new(struct agent_pvt *p, int state, const char *linkedid)
{
struct ast_channel *tmp;
int alreadylocked;
@ -997,9 +997,9 @@ static struct ast_channel *agent_new(struct agent_pvt *p, int state)
}
#endif
if (p->pending)
tmp = ast_channel_alloc(0, state, 0, 0, "", p->chan ? p->chan->exten:"", p->chan ? p->chan->context:"", 0, "Agent/P%s-%d", p->agent, (int) ast_random() & 0xffff);
tmp = ast_channel_alloc(0, state, 0, 0, "", p->chan ? p->chan->exten:"", p->chan ? p->chan->context:"", linkedid, 0, "Agent/P%s-%d", p->agent, (int) ast_random() & 0xffff);
else
tmp = ast_channel_alloc(0, state, 0, 0, "", p->chan ? p->chan->exten:"", p->chan ? p->chan->context:"", 0, "Agent/%s", p->agent);
tmp = ast_channel_alloc(0, state, 0, 0, "", p->chan ? p->chan->exten:"", p->chan ? p->chan->context:"", linkedid, 0, "Agent/%s", p->agent);
if (!tmp) {
ast_log(LOG_WARNING, "Unable to allocate agent channel structure\n");
return NULL;
@ -1249,7 +1249,7 @@ static int check_availability(struct agent_pvt *newlyavailable, int needlock)
if (!p->abouttograb && p->pending && ((p->group && (newlyavailable->group & p->group)) || !strcmp(p->agent, newlyavailable->agent))) {
ast_debug(1, "Call '%s' looks like a winner for agent '%s'\n", p->owner->name, newlyavailable->agent);
/* We found a pending call, time to merge */
chan = agent_new(newlyavailable, AST_STATE_DOWN);
chan = agent_new(newlyavailable, AST_STATE_DOWN, p->owner ? p->owner->linkedid : NULL);
parent = p->owner;
p->abouttograb = 1;
ast_mutex_unlock(&p->lock);
@ -1334,7 +1334,7 @@ static int check_beep(struct agent_pvt *newlyavailable, int needlock)
}
/*! \brief Part of the Asterisk PBX interface */
static struct ast_channel *agent_request(const char *type, int format, void *data, int *cause)
static struct ast_channel *agent_request(const char *type, int format, const struct ast_channel* requestor, void *data, int *cause)
{
struct agent_pvt *p;
struct ast_channel *chan = NULL;
@ -1367,7 +1367,7 @@ static struct ast_channel *agent_request(const char *type, int format, void *dat
/* Agent must be registered, but not have any active call, and not be in a waiting state */
if (!p->owner && p->chan) {
/* Fixed agent */
chan = agent_new(p, AST_STATE_DOWN);
chan = agent_new(p, AST_STATE_DOWN, requestor ? requestor->linkedid : NULL);
}
if (chan) {
ast_mutex_unlock(&p->lock);
@ -1390,7 +1390,7 @@ static struct ast_channel *agent_request(const char *type, int format, void *dat
/* Agent must be registered, but not have any active call, and not be in a waiting state */
if (!p->owner && p->chan) {
/* Could still get a fixed agent */
chan = agent_new(p, AST_STATE_DOWN);
chan = agent_new(p, AST_STATE_DOWN, requestor ? requestor->linkedid : NULL);
}
if (chan) {
ast_mutex_unlock(&p->lock);
@ -1409,7 +1409,7 @@ static struct ast_channel *agent_request(const char *type, int format, void *dat
ast_debug(1, "Creating place holder for '%s'\n", s);
p = add_agent(data, 1);
p->group = groupmatch;
chan = agent_new(p, AST_STATE_DOWN);
chan = agent_new(p, AST_STATE_DOWN, requestor ? requestor->linkedid : NULL);
if (!chan)
ast_log(LOG_WARNING, "Weird... Fix this to drop the unused pending agent\n");
} else {

View File

@ -130,7 +130,7 @@ static int writedev = -1;
static int autoanswer = 1;
static struct ast_channel *alsa_request(const char *type, int format, void *data, int *cause);
static struct ast_channel *alsa_request(const char *type, int format, const struct ast_channel *requestor, void *data, int *cause);
static int alsa_digit(struct ast_channel *c, char digit, unsigned int duration);
static int alsa_text(struct ast_channel *c, const char *text);
static int alsa_hangup(struct ast_channel *c);
@ -532,11 +532,11 @@ static int alsa_indicate(struct ast_channel *chan, int cond, const void *data, s
return res;
}
static struct ast_channel *alsa_new(struct chan_alsa_pvt *p, int state)
static struct ast_channel *alsa_new(struct chan_alsa_pvt *p, int state, const char *linkedid)
{
struct ast_channel *tmp = NULL;
if (!(tmp = ast_channel_alloc(1, state, 0, 0, "", p->exten, p->context, 0, "ALSA/%s", indevname)))
if (!(tmp = ast_channel_alloc(1, state, 0, 0, "", p->exten, p->context, linkedid, 0, "ALSA/%s", indevname)))
return NULL;
tmp->tech = &alsa_tech;
@ -565,7 +565,7 @@ static struct ast_channel *alsa_new(struct chan_alsa_pvt *p, int state)
return tmp;
}
static struct ast_channel *alsa_request(const char *type, int fmt, void *data, int *cause)
static struct ast_channel *alsa_request(const char *type, int fmt, const struct ast_channel *requestor, void *data, int *cause)
{
int oldformat = fmt;
struct ast_channel *tmp = NULL;
@ -580,7 +580,7 @@ static struct ast_channel *alsa_request(const char *type, int fmt, void *data, i
if (alsa.owner) {
ast_log(LOG_NOTICE, "Already have a call on the ALSA channel\n");
*cause = AST_CAUSE_BUSY;
} else if (!(tmp = alsa_new(&alsa, AST_STATE_DOWN))) {
} else if (!(tmp = alsa_new(&alsa, AST_STATE_DOWN, requestor ? requestor->linkedid : NULL))) {
ast_log(LOG_WARNING, "Unable to create new ALSA channel\n");
}
@ -833,7 +833,7 @@ static char *console_dial(struct ast_cli_entry *e, int cmd, struct ast_cli_args
ast_copy_string(alsa.exten, mye, sizeof(alsa.exten));
ast_copy_string(alsa.context, myc, sizeof(alsa.context));
hookstate = 1;
alsa_new(&alsa, AST_STATE_RINGING);
alsa_new(&alsa, AST_STATE_RINGING, NULL);
} else
ast_cli(a->fd, "No such extension '%s' in context '%s'\n", mye, myc);
}

View File

@ -46,7 +46,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/app.h"
#include "asterisk/bridging.h"
static struct ast_channel *bridge_request(const char *type, int format, void *data, int *cause);
static struct ast_channel *bridge_request(const char *type, int format, const struct ast_channel *requestor, void *data, int *cause);
static int bridge_call(struct ast_channel *ast, char *dest, int timeout);
static int bridge_hangup(struct ast_channel *ast);
static struct ast_frame *bridge_read(struct ast_channel *ast);
@ -189,7 +189,7 @@ static int bridge_hangup(struct ast_channel *ast)
}
/*! \brief Called when we want to place a call somewhere, but not actually call it... yet */
static struct ast_channel *bridge_request(const char *type, int format, void *data, int *cause)
static struct ast_channel *bridge_request(const char *type, int format, const struct ast_channel *requestor, void *data, int *cause)
{
struct bridge_pvt *p = NULL;
@ -199,11 +199,11 @@ static struct ast_channel *bridge_request(const char *type, int format, void *da
}
/* Try to grab two Asterisk channels to use as input and output channels */
if (!(p->input = ast_channel_alloc(1, AST_STATE_UP, 0, 0, "", "", "", 0, "Bridge/%p-input", p))) {
if (!(p->input = ast_channel_alloc(1, AST_STATE_UP, 0, 0, "", "", "", requestor ? requestor->linkedid : NULL, 0, "Bridge/%p-input", p))) {
ast_free(p);
return NULL;
}
if (!(p->output = ast_channel_alloc(1, AST_STATE_UP, 0, 0, "", "", "", 0, "Bridge/%p-output", p))) {
if (!(p->output = ast_channel_alloc(1, AST_STATE_UP, 0, 0, "", "", "", requestor ? requestor->linkedid : NULL, 0, "Bridge/%p-output", p))) {
p->input = ast_channel_release(p->input);
ast_free(p);
return NULL;

View File

@ -189,8 +189,8 @@ static struct ast_jb_conf default_jbconf = {
static struct ast_jb_conf global_jbconf;
/*! Channel Technology Callbacks @{ */
static struct ast_channel *console_request(const char *type, int format,
void *data, int *cause);
static struct ast_channel *console_request(const char *type, int format,
const struct ast_channel *requestor, void *data, int *cause);
static int console_digit_begin(struct ast_channel *c, char digit);
static int console_digit_end(struct ast_channel *c, char digit, unsigned int duration);
static int console_text(struct ast_channel *c, const char *text);
@ -413,12 +413,12 @@ static int stop_stream(struct console_pvt *pvt)
/*!
* \note Called with the pvt struct locked
*/
static struct ast_channel *console_new(struct console_pvt *pvt, const char *ext, const char *ctx, int state)
static struct ast_channel *console_new(struct console_pvt *pvt, const char *ext, const char *ctx, int state, const char *linkedid)
{
struct ast_channel *chan;
if (!(chan = ast_channel_alloc(1, state, pvt->cid_num, pvt->cid_name, NULL,
ext, ctx, 0, "Console/%s", pvt->name))) {
ext, ctx, linkedid, 0, "Console/%s", pvt->name))) {
return NULL;
}
@ -447,7 +447,7 @@ static struct ast_channel *console_new(struct console_pvt *pvt, const char *ext,
return chan;
}
static struct ast_channel *console_request(const char *type, int format, void *data, int *cause)
static struct ast_channel *console_request(const char *type, int format, const struct ast_channel *requestor, void *data, int *cause)
{
int oldformat = format;
struct ast_channel *chan = NULL;
@ -471,7 +471,7 @@ static struct ast_channel *console_request(const char *type, int format, void *d
}
console_pvt_lock(pvt);
chan = console_new(pvt, NULL, NULL, AST_STATE_DOWN);
chan = console_new(pvt, NULL, NULL, AST_STATE_DOWN, requestor ? requestor->linkedid : NULL);
console_pvt_unlock(pvt);
if (!chan)
@ -833,7 +833,7 @@ static char *cli_console_dial(struct ast_cli_entry *e, int cmd, struct ast_cli_a
if (ast_exists_extension(NULL, myc, mye, 1, NULL)) {
console_pvt_lock(pvt);
pvt->hookstate = 1;
console_new(pvt, mye, myc, AST_STATE_RINGING);
console_new(pvt, mye, myc, AST_STATE_RINGING, NULL);
console_pvt_unlock(pvt);
} else
ast_cli(a->fd, "No such extension '%s' in context '%s'\n", mye, myc);

View File

@ -1,7 +1,7 @@
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 1999 - 2006, Digium, Inc.
* Copyright (C) 1999 - 2008, Digium, Inc.
*
* Mark Spencer <markster@digium.com>
*
@ -91,6 +91,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/adsi.h"
#include "asterisk/cli.h"
#include "asterisk/cdr.h"
#include "asterisk/cel.h"
#include "asterisk/features.h"
#include "asterisk/musiconhold.h"
#include "asterisk/say.h"
@ -1388,7 +1389,7 @@ static struct dahdi_chan_conf dahdi_chan_conf_default(void)
}
static struct ast_channel *dahdi_request(const char *type, int format, void *data, int *cause);
static struct ast_channel *dahdi_request(const char *type, int format, const struct ast_channel *requestor, void *data, int *cause);
static int dahdi_digit_begin(struct ast_channel *ast, char digit);
static int dahdi_digit_end(struct ast_channel *ast, char digit, unsigned int duration);
static int dahdi_sendtext(struct ast_channel *c, const char *text);
@ -1978,14 +1979,14 @@ static void my_swap_subchannels(void *pvt, enum analog_sub a, struct ast_channel
return;
}
static struct ast_channel *dahdi_new(struct dahdi_pvt *, int, int, int, int, int);
static struct ast_channel *dahdi_new(struct dahdi_pvt *, int, int, int, int, int, const char *);
static struct ast_channel *my_new_analog_ast_channel(void *pvt, int state, int startpbx, enum analog_sub sub)
static struct ast_channel *my_new_analog_ast_channel(void *pvt, int state, int startpbx, enum analog_sub sub, const struct ast_channel *requestor)
{
struct dahdi_pvt *p = pvt;
int dsub = analogsub_to_dahdisub(sub);
return dahdi_new(p, state, startpbx, dsub, 0, 0);
return dahdi_new(p, state, startpbx, dsub, 0, 0, requestor ? requestor->linkedid : "");
}
#if defined(HAVE_PRI) || defined(HAVE_SS7)
@ -2000,7 +2001,7 @@ static int dahdi_setlaw(int dfd, int law)
#endif
#ifdef HAVE_PRI
static struct ast_channel *my_new_pri_ast_channel(void *pvt, int state, int startpbx, enum sig_pri_law law, int transfercapability, char *exten)
static struct ast_channel *my_new_pri_ast_channel(void *pvt, int state, int startpbx, enum sig_pri_law law, int transfercapability, char *exten, const struct ast_channel *requestor)
{
struct dahdi_pvt *p = pvt;
int audio;
@ -2028,7 +2029,7 @@ static struct ast_channel *my_new_pri_ast_channel(void *pvt, int state, int star
newlaw = DAHDI_LAW_MULAW;
break;
}
return dahdi_new(p, state, startpbx, SUB_REAL, newlaw, transfercapability);
return dahdi_new(p, state, startpbx, SUB_REAL, newlaw, transfercapability, requestor ? requestor->linkedid : "");
}
#endif
@ -2595,7 +2596,6 @@ static void dahdi_queue_frame(struct dahdi_pvt *p, struct ast_frame *f, void *da
#endif
}
static struct ast_channel *dahdi_new(struct dahdi_pvt *, int, int, int, int, int);
#ifdef HAVE_OPENR2
static int dahdi_r2_answer(struct dahdi_pvt *p)
@ -2764,7 +2764,7 @@ static void dahdi_r2_on_call_offered(openr2_chan_t *r2chan, const char *ani, con
}
if (!p->mfcr2_accept_on_offer) {
/* The user wants us to start the PBX thread right away without accepting the call first */
c = dahdi_new(p, AST_STATE_RING, 1, SUB_REAL, DAHDI_LAW_ALAW, 0);
c = dahdi_new(p, AST_STATE_RING, 1, SUB_REAL, DAHDI_LAW_ALAW, 0, NULL);
if (c) {
dahdi_r2_update_monitor_count(p->mfcr2, 0);
/* Done here, don't disable reading now since we still need to generate MF tones to accept
@ -2815,7 +2815,7 @@ static void dahdi_r2_on_call_accepted(openr2_chan_t *r2chan, openr2_call_mode_t
}
return;
}
c = dahdi_new(p, AST_STATE_RING, 1, SUB_REAL, DAHDI_LAW_ALAW, 0);
c = dahdi_new(p, AST_STATE_RING, 1, SUB_REAL, DAHDI_LAW_ALAW, 0, NULL);
if (c) {
dahdi_r2_update_monitor_count(p->mfcr2, 0);
/* chan_dahdi will take care of reading from now on in the PBX thread, tell the
@ -6747,7 +6747,7 @@ static struct ast_frame *dahdi_handle_event(struct ast_channel *ast)
goto winkflashdone;
}
/* Make new channel */
chan = dahdi_new(p, AST_STATE_RESERVED, 0, SUB_THREEWAY, 0, 0);
chan = dahdi_new(p, AST_STATE_RESERVED, 0, SUB_THREEWAY, 0, 0, 0);
if (p->dahditrcallerid) {
if (!p->origcid_num)
p->origcid_num = ast_strdup(p->cid_num);
@ -7102,6 +7102,12 @@ static struct ast_frame *__dahdi_exception(struct ast_channel *ast)
return f;
}
f = dahdi_handle_event(ast);
/* tell the cdr this zap device hung up */
if (f == NULL) {
ast_set_hangupsource(ast, ast->name, 0);
}
return f;
}
@ -7722,7 +7728,7 @@ static int dahdi_indicate(struct ast_channel *chan, int condition, const void *d
return res;
}
static struct ast_channel *dahdi_new(struct dahdi_pvt *i, int state, int startpbx, int idx, int law, int transfercapability)
static struct ast_channel *dahdi_new(struct dahdi_pvt *i, int state, int startpbx, int idx, int law, int transfercapability, const char *linkedid)
{
struct ast_channel *tmp;
int deflaw;
@ -7749,7 +7755,7 @@ static struct ast_channel *dahdi_new(struct dahdi_pvt *i, int state, int startpb
}
y++;
} while (x < 3);
tmp = ast_channel_alloc(0, state, i->cid_num, i->cid_name, i->accountcode, i->exten, i->context, i->amaflags, "DAHDI/%s", ast_str_buffer(chan_name));
tmp = ast_channel_alloc(0, state, i->cid_num, i->cid_name, i->accountcode, i->exten, i->context, linkedid, i->amaflags, "DAHDI/%s", ast_str_buffer(chan_name));
if (!tmp)
return NULL;
tmp->tech = &dahdi_tech;
@ -9158,7 +9164,7 @@ static void *mwi_thread(void *data)
restore_gains(mtd->pvt);
mtd->pvt->ringt = mtd->pvt->ringt_base;
if ((chan = dahdi_new(mtd->pvt, AST_STATE_RING, 0, SUB_REAL, 0, 0))) {
if ((chan = dahdi_new(mtd->pvt, AST_STATE_RING, 0, SUB_REAL, 0, 0, NULL))) {
int result;
if (analog_lib_handles(mtd->pvt->sig, mtd->pvt->radio, mtd->pvt->oprmode)) {
result = analog_ss_thread_start(mtd->pvt->sig_pvt, chan);
@ -9483,7 +9489,7 @@ static int handle_init_event(struct dahdi_pvt *i, int event)
dahdi_enable_ec(i);
/* The channel is immediately up. Start right away */
res = tone_zone_play_tone(i->subs[SUB_REAL].dfd, DAHDI_TONE_RINGTONE);
chan = dahdi_new(i, AST_STATE_RING, 1, SUB_REAL, 0, 0);
chan = dahdi_new(i, AST_STATE_RING, 1, SUB_REAL, 0, 0, NULL);
if (!chan) {
ast_log(LOG_WARNING, "Unable to start PBX on channel %d\n", i->channel);
res = tone_zone_play_tone(i->subs[SUB_REAL].dfd, DAHDI_TONE_CONGESTION);
@ -9492,7 +9498,7 @@ static int handle_init_event(struct dahdi_pvt *i, int event)
}
} else {
/* Check for callerid, digits, etc */
chan = dahdi_new(i, AST_STATE_RESERVED, 0, SUB_REAL, 0, 0);
chan = dahdi_new(i, AST_STATE_RESERVED, 0, SUB_REAL, 0, 0, NULL);
if (chan) {
if (has_voicemail(i))
res = tone_zone_play_tone(i->subs[SUB_REAL].dfd, DAHDI_TONE_STUTTER);
@ -9535,9 +9541,9 @@ static int handle_init_event(struct dahdi_pvt *i, int event)
case SIG_SF:
/* Check for callerid, digits, etc */
if (i->cid_start == CID_START_POLARITY_IN) {
chan = dahdi_new(i, AST_STATE_PRERING, 0, SUB_REAL, 0, 0);
chan = dahdi_new(i, AST_STATE_PRERING, 0, SUB_REAL, 0, 0, NULL);
} else {
chan = dahdi_new(i, AST_STATE_RING, 0, SUB_REAL, 0, 0);
chan = dahdi_new(i, AST_STATE_RING, 0, SUB_REAL, 0, 0, NULL);
}
if (!chan) {
@ -9639,7 +9645,7 @@ static int handle_init_event(struct dahdi_pvt *i, int event)
ast_verb(2, "Starting post polarity "
"CID detection on channel %d\n",
i->channel);
chan = dahdi_new(i, AST_STATE_PRERING, 0, SUB_REAL, 0, 0);
chan = dahdi_new(i, AST_STATE_PRERING, 0, SUB_REAL, 0, 0, NULL);
if (!chan) {
ast_log(LOG_WARNING, "Cannot allocate new structure on channel %d\n", i->channel);
} else if (ast_pthread_create_detached(&threadid, NULL, analog_ss_thread, chan)) {
@ -10723,7 +10729,7 @@ static struct dahdi_pvt *mkintf(int channel, const struct dahdi_chan_conf *conf,
strsep(&context, "@");
if (ast_strlen_zero(context))
context = "default";
tmp->mwi_event_sub = ast_event_subscribe(AST_EVENT_MWI, mwi_event_cb, NULL,
tmp->mwi_event_sub = ast_event_subscribe(AST_EVENT_MWI, mwi_event_cb, "Dahdi MWI subscription", NULL,
AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mailbox,
AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, context,
AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_EXISTS,
@ -11005,7 +11011,7 @@ static struct dahdi_pvt *duplicate_pseudo(struct dahdi_pvt *src)
return p;
}
static struct ast_channel *dahdi_request(const char *type, int format, void *data, int *cause)
static struct ast_channel *dahdi_request(const char *type, int format, const struct ast_channel *requestor, void *data, int *cause)
{
ast_group_t groupmatch = 0;
int channelmatch = -1;
@ -11140,15 +11146,15 @@ static struct ast_channel *dahdi_request(const char *type, int format, void *dat
}
p->outgoing = 1;
if (analog_lib_handles(p->sig, p->radio, p->oprmode)) {
tmp = analog_request(p->sig_pvt, &callwait);
}
tmp = analog_request(p->sig_pvt, &callwait, requestor);
}
#ifdef HAVE_PRI
else if (p->sig == SIG_PRI || p->sig == SIG_BRI || p->sig == SIG_BRI_PTMP) {
tmp = sig_pri_request(p->sig_pvt, SIG_PRI_DEFLAW);
tmp = sig_pri_request(p->sig_pvt, SIG_PRI_DEFLAW, requestor);
}
#endif
else {
tmp = dahdi_new(p, AST_STATE_RESERVED, 0, p->owner ? SUB_CALLWAIT : SUB_REAL, 0, 0);
tmp = dahdi_new(p, AST_STATE_RESERVED, 0, p->owner ? SUB_CALLWAIT : SUB_REAL, 0, 0, requestor ? requestor->linkedid : "");
}
/* Make special notes */
@ -11370,7 +11376,7 @@ static void ss7_start_call(struct dahdi_pvt *p, struct dahdi_ss7 *linkset)
}
ast_mutex_unlock(&linkset->lock);
c = dahdi_new(p, AST_STATE_RING, 1, SUB_REAL, law, 0);
c = dahdi_new(p, AST_STATE_RING, 1, SUB_REAL, law, 0, NULL);
if (!c) {
ast_log(LOG_WARNING, "Unable to start PBX on CIC %d\n", p->cic);

View File

@ -166,7 +166,7 @@ static int global_capability = AST_FORMAT_ULAW | AST_FORMAT_ALAW | AST_FORMAT_GS
AST_MUTEX_DEFINE_STATIC(gtalklock); /*!< Protect the interface list (of gtalk_pvt's) */
/* Forward declarations */
static struct ast_channel *gtalk_request(const char *type, int format, void *data, int *cause);
static struct ast_channel *gtalk_request(const char *type, int format, const struct ast_channel *requestor, void *data, int *cause);
static int gtalk_digit(struct ast_channel *ast, char digit, unsigned int duration);
static int gtalk_digit_begin(struct ast_channel *ast, char digit);
static int gtalk_digit_end(struct ast_channel *ast, char digit, unsigned int duration);
@ -986,7 +986,7 @@ static struct gtalk_pvt *gtalk_alloc(struct gtalk *client, const char *us, const
}
/*! \brief Start new gtalk channel */
static struct ast_channel *gtalk_new(struct gtalk *client, struct gtalk_pvt *i, int state, const char *title)
static struct ast_channel *gtalk_new(struct gtalk *client, struct gtalk_pvt *i, int state, const char *title, const char *linkedid)
{
struct ast_channel *tmp;
int fmt;
@ -997,7 +997,7 @@ static struct ast_channel *gtalk_new(struct gtalk *client, struct gtalk_pvt *i,
n2 = title;
else
n2 = i->us;
tmp = ast_channel_alloc(1, state, i->cid_num, i->cid_name, client->accountcode, i->exten, client->context, client->amaflags, "Gtalk/%s-%04lx", n2, ast_random() & 0xffff);
tmp = ast_channel_alloc(1, state, i->cid_num, i->cid_name, linkedid, client->accountcode, i->exten, client->context, client->amaflags, "Gtalk/%s-%04lx", n2, ast_random() & 0xffff);
if (!tmp) {
ast_log(LOG_WARNING, "Unable to allocate Gtalk channel structure!\n");
return NULL;
@ -1191,7 +1191,7 @@ static int gtalk_newcall(struct gtalk *client, ikspak *pak)
return -1;
}
chan = gtalk_new(client, p, AST_STATE_DOWN, pak->from->user);
chan = gtalk_new(client, p, AST_STATE_DOWN, pak->from->user, NULL);
if (!chan) {
gtalk_free_pvt(client, p);
return -1;
@ -1634,7 +1634,7 @@ static int gtalk_hangup(struct ast_channel *ast)
}
/*! \brief Part of PBX interface */
static struct ast_channel *gtalk_request(const char *type, int format, void *data, int *cause)
static struct ast_channel *gtalk_request(const char *type, int format, const struct ast_channel *requestor, void *data, int *cause)
{
struct gtalk_pvt *p = NULL;
struct gtalk *client = NULL;
@ -1673,7 +1673,7 @@ static struct ast_channel *gtalk_request(const char *type, int format, void *dat
ASTOBJ_WRLOCK(client);
p = gtalk_alloc(client, strchr(sender, '@') ? sender : client->connection->jid->full, strchr(to, '@') ? to : client->user, NULL);
if (p)
chan = gtalk_new(client, p, AST_STATE_DOWN, to);
chan = gtalk_new(client, p, AST_STATE_DOWN, to, requestor ? requestor->linkedid : NULL);
ASTOBJ_UNLOCK(client);
return chan;

View File

@ -230,7 +230,7 @@ static void delete_users(void);
static void delete_aliases(void);
static void prune_peers(void);
static struct ast_channel *oh323_request(const char *type, int format, void *data, int *cause);
static struct ast_channel *oh323_request(const char *type, int format, const struct ast_channel *requestor, void *data, int *cause);
static int oh323_digit_begin(struct ast_channel *c, char digit);
static int oh323_digit_end(struct ast_channel *c, char digit, unsigned int duration);
static int oh323_call(struct ast_channel *c, char *dest, int timeout);
@ -994,7 +994,7 @@ static int __oh323_rtp_create(struct oh323_pvt *pvt)
}
/*! \brief Private structure should be locked on a call */
static struct ast_channel *__oh323_new(struct oh323_pvt *pvt, int state, const char *host)
static struct ast_channel *__oh323_new(struct oh323_pvt *pvt, int state, const char *host, const char *linkedid)
{
struct ast_channel *ch;
char *cid_num, *cid_name;
@ -1012,7 +1012,7 @@ static struct ast_channel *__oh323_new(struct oh323_pvt *pvt, int state, const c
/* Don't hold a oh323_pvt lock while we allocate a chanel */
ast_mutex_unlock(&pvt->lock);
ch = ast_channel_alloc(1, state, cid_num, cid_name, pvt->accountcode, pvt->exten, pvt->context, pvt->amaflags, "H323/%s", host);
ch = ast_channel_alloc(1, state, cid_num, cid_name, pvt->accountcode, pvt->exten, pvt->context, linkedid, pvt->amaflags, "H323/%s", host);
/* Update usage counter */
ast_module_ref(ast_module_info->self);
ast_mutex_lock(&pvt->lock);
@ -1717,7 +1717,7 @@ static int create_addr(struct oh323_pvt *pvt, char *opeer)
return 0;
}
}
static struct ast_channel *oh323_request(const char *type, int format, void *data, int *cause)
static struct ast_channel *oh323_request(const char *type, int format, const struct ast_channel *requestor, void *data, int *cause)
{
int oldformat;
struct oh323_pvt *pvt;
@ -1793,7 +1793,7 @@ static struct ast_channel *oh323_request(const char *type, int format, void *dat
ast_mutex_unlock(&caplock);
ast_mutex_lock(&pvt->lock);
tmpc = __oh323_new(pvt, AST_STATE_DOWN, tmp1);
tmpc = __oh323_new(pvt, AST_STATE_DOWN, tmp1, requestor ? requestor->linkedid : NULL);
ast_mutex_unlock(&pvt->lock);
if (!tmpc) {
oh323_destroy(pvt);
@ -2277,7 +2277,7 @@ static int answer_call(unsigned call_reference, const char *token)
}
/* allocate a channel and tell asterisk about it */
c = __oh323_new(pvt, AST_STATE_RINGING, pvt->cd.call_token);
c = __oh323_new(pvt, AST_STATE_RINGING, pvt->cd.call_token, NULL);
/* And release when done */
ast_mutex_unlock(&pvt->lock);

View File

@ -1104,7 +1104,7 @@ static int send_command_final(struct chan_iax2_pvt *, char, int, unsigned int, c
static int send_command_immediate(struct chan_iax2_pvt *, char, int, unsigned int, const unsigned char *, int, int);
static int send_command_locked(unsigned short callno, char, int, unsigned int, const unsigned char *, int, int);
static int send_command_transfer(struct chan_iax2_pvt *, char, int, unsigned int, const unsigned char *, int);
static struct ast_channel *iax2_request(const char *type, int format, void *data, int *cause);
static struct ast_channel *iax2_request(const char *type, int format, const struct ast_channel *requestor, void *data, int *cause);
static struct ast_frame *iax2_read(struct ast_channel *c);
static struct iax2_peer *build_peer(const char *name, struct ast_variable *v, struct ast_variable *alt, int temponly);
static struct iax2_user *build_user(const char *name, struct ast_variable *v, struct ast_variable *alt, int temponly);
@ -4516,7 +4516,7 @@ static int iax2_getpeertrunk(struct sockaddr_in sin)
}
/*! \brief Create new call, interface with the PBX core */
static struct ast_channel *ast_iax2_new(int callno, int state, int capability)
static struct ast_channel *ast_iax2_new(int callno, int state, int capability, const char *linkedid)
{
struct ast_channel *tmp;
struct chan_iax2_pvt *i;
@ -4529,7 +4529,7 @@ static struct ast_channel *ast_iax2_new(int callno, int state, int capability)
/* Don't hold call lock */
ast_mutex_unlock(&iaxsl[callno]);
tmp = ast_channel_alloc(1, state, i->cid_num, i->cid_name, i->accountcode, i->exten, i->context, i->amaflags, "IAX2/%s-%d", i->host, i->callno);
tmp = ast_channel_alloc(1, state, i->cid_num, i->cid_name, i->accountcode, i->exten, i->context, linkedid, i->amaflags, "IAX2/%s-%d", i->host, i->callno);
ast_mutex_lock(&iaxsl[callno]);
if (i != iaxs[callno]) {
if (tmp) {
@ -8003,8 +8003,8 @@ static int iax_park(struct ast_channel *chan1, struct ast_channel *chan2)
struct iax_dual *d;
struct ast_channel *chan1m, *chan2m;
pthread_t th;
chan1m = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, chan2->accountcode, chan1->exten, chan1->context, chan1->amaflags, "Parking/%s", chan1->name);
chan2m = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, chan2->accountcode, chan2->exten, chan2->context, chan2->amaflags, "IAXPeer/%s",chan2->name);
chan1m = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, chan2->accountcode, chan1->exten, chan1->context, chan1->linkedid, chan1->amaflags, "Parking/%s", chan1->name);
chan2m = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, chan2->accountcode, chan2->exten, chan2->context, chan2->linkedid, chan2->amaflags, "IAXPeer/%s", chan2->name);
if (chan2m && chan1m) {
/* Make formats okay */
chan1m->readformat = chan1->readformat;
@ -8512,6 +8512,35 @@ static struct ast_custom_function iaxvar_function = {
.write = acf_iaxvar_write,
};
static void set_hangup_source_and_cause(int callno, unsigned char causecode)
{
int locked = 0;
struct ast_channel *owner=NULL;
do {
if (ast_channel_trylock(iaxs[callno]->owner)) {
DEADLOCK_AVOIDANCE(&iaxsl[callno]);
}
else {
locked = 1;
owner = iaxs[callno]->owner;
}
}
while (!locked && iaxs[callno] && iaxs[callno]->owner);
if (iaxs[callno] && iaxs[callno]->owner) {
if (causecode) {
iaxs[callno]->owner->hangupcause = causecode;
}
ast_set_hangupsource(iaxs[callno]->owner, iaxs[callno]->owner->name, 0);
ast_channel_unlock(owner);
}
if (locked) {
ast_channel_unlock(owner);
}
}
static int socket_process(struct iax2_thread *thread)
{
struct sockaddr_in sin;
@ -8853,7 +8882,7 @@ static int socket_process(struct iax2_thread *thread)
(f.frametype == AST_FRAME_IAX)) {
if (ast_test_flag64(iaxs[fr->callno], IAX_DELAYPBXSTART)) {
ast_clear_flag64(iaxs[fr->callno], IAX_DELAYPBXSTART);
if (!ast_iax2_new(fr->callno, AST_STATE_RING, iaxs[fr->callno]->chosenformat)) {
if (!ast_iax2_new(fr->callno, AST_STATE_RING, iaxs[fr->callno]->chosenformat, NULL)) {
ast_mutex_unlock(&iaxsl[fr->callno]);
return 1;
}
@ -9276,17 +9305,28 @@ retryowner:
case IAX_COMMAND_HANGUP:
ast_set_flag64(iaxs[fr->callno], IAX_ALREADYGONE);
ast_debug(1, "Immediately destroying %d, having received hangup\n", fr->callno);
/* Set hangup cause according to remote */
if (ies.causecode && iaxs[fr->callno]->owner)
iaxs[fr->callno]->owner->hangupcause = ies.causecode;
/* Set hangup cause according to remote and hangupsource */
if (iaxs[fr->callno]->owner) {
set_hangup_source_and_cause(fr->callno, ies.causecode);
if (!iaxs[fr->callno]) {
ast_mutex_unlock(&iaxsl[fr->callno]);
return 1;
}
}
/* Send ack immediately, before we destroy */
send_command_immediate(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_ACK, fr->ts, NULL, 0,fr->iseqno);
iax2_destroy(fr->callno);
break;
case IAX_COMMAND_REJECT:
/* Set hangup cause according to remote */
if (ies.causecode && iaxs[fr->callno]->owner)
iaxs[fr->callno]->owner->hangupcause = ies.causecode;
/* Set hangup cause according to remote and hangup source */
if (iaxs[fr->callno]->owner) {
set_hangup_source_and_cause(fr->callno, ies.causecode);
if (!iaxs[fr->callno]) {
ast_mutex_unlock(&iaxsl[fr->callno]);
return 1;
}
}
if (!ast_test_flag64(iaxs[fr->callno], IAX_PROVISION)) {
if (iaxs[fr->callno]->owner && authdebug)
@ -9668,7 +9708,7 @@ retryowner2:
using_prefs);
ast_set_flag(&iaxs[fr->callno]->state, IAX_STATE_STARTED);
if (!(c = ast_iax2_new(fr->callno, AST_STATE_RING, format)))
if (!(c = ast_iax2_new(fr->callno, AST_STATE_RING, format, NULL)))
iax2_destroy(fr->callno);
else if (ies.vars) {
struct ast_datastore *variablestore;
@ -9737,7 +9777,7 @@ immediatedial:
ast_verb(3, "Accepting DIAL from %s, formats = 0x%x\n", ast_inet_ntoa(sin.sin_addr), iaxs[fr->callno]->peerformat);
ast_set_flag(&iaxs[fr->callno]->state, IAX_STATE_STARTED);
send_command(iaxs[fr->callno], AST_FRAME_CONTROL, AST_CONTROL_PROGRESS, 0, NULL, 0, -1);
if (!(c = ast_iax2_new(fr->callno, AST_STATE_RING, iaxs[fr->callno]->peerformat)))
if (!(c = ast_iax2_new(fr->callno, AST_STATE_RING, iaxs[fr->callno]->peerformat, NULL)))
iax2_destroy(fr->callno);
else if (ies.vars) {
struct ast_datastore *variablestore;
@ -10570,7 +10610,7 @@ static void free_context(struct iax2_context *con)
}
}
static struct ast_channel *iax2_request(const char *type, int format, void *data, int *cause)
static struct ast_channel *iax2_request(const char *type, int format, const struct ast_channel *requestor, void *data, int *cause)
{
int callno;
int res;
@ -10622,7 +10662,7 @@ static struct ast_channel *iax2_request(const char *type, int format, void *data
if (cai.found)
ast_string_field_set(iaxs[callno], host, pds.peer);
c = ast_iax2_new(callno, AST_STATE_DOWN, cai.capability);
c = ast_iax2_new(callno, AST_STATE_DOWN, cai.capability, requestor ? requestor->linkedid : NULL);
ast_mutex_unlock(&iaxsl[callno]);
@ -11086,7 +11126,7 @@ static struct iax2_peer *build_peer(const char *name, struct ast_variable *v, st
strsep(&context, "@");
if (ast_strlen_zero(context))
context = "default";
peer->mwi_event_sub = ast_event_subscribe(AST_EVENT_MWI, mwi_event_cb, NULL,
peer->mwi_event_sub = ast_event_subscribe(AST_EVENT_MWI, mwi_event_cb, "IAX MWI subscription", NULL,
AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mailbox,
AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, context,
AST_EVENT_IE_END);

View File

@ -168,7 +168,7 @@ static int global_capability = AST_FORMAT_ULAW | AST_FORMAT_ALAW | AST_FORMAT_GS
AST_MUTEX_DEFINE_STATIC(jinglelock); /*!< Protect the interface list (of jingle_pvt's) */
/* Forward declarations */
static struct ast_channel *jingle_request(const char *type, int format, void *data, int *cause);
static struct ast_channel *jingle_request(const char *type, int format, const struct ast_channel *requestor, void *data, int *cause);
static int jingle_digit_begin(struct ast_channel *ast, char digit);
static int jingle_digit_end(struct ast_channel *ast, char digit, unsigned int duration);
static int jingle_call(struct ast_channel *ast, char *dest, int timeout);
@ -789,7 +789,7 @@ static struct jingle_pvt *jingle_alloc(struct jingle *client, const char *from,
}
/*! \brief Start new jingle channel */
static struct ast_channel *jingle_new(struct jingle *client, struct jingle_pvt *i, int state, const char *title)
static struct ast_channel *jingle_new(struct jingle *client, struct jingle_pvt *i, int state, const char *title, const char *linkedid)
{
struct ast_channel *tmp;
int fmt;
@ -800,7 +800,7 @@ static struct ast_channel *jingle_new(struct jingle *client, struct jingle_pvt *
str = title;
else
str = i->them;
tmp = ast_channel_alloc(1, state, i->cid_num, i->cid_name, "", "", "", 0, "Jingle/%s-%04lx", str, ast_random() & 0xffff);
tmp = ast_channel_alloc(1, state, i->cid_num, i->cid_name, "", "", "", linkedid, 0, "Jingle/%s-%04lx", str, ast_random() & 0xffff);
if (!tmp) {
ast_log(LOG_WARNING, "Unable to allocate Jingle channel structure!\n");
return NULL;
@ -980,7 +980,7 @@ static int jingle_newcall(struct jingle *client, ikspak *pak)
ast_log(LOG_WARNING, "Unable to allocate jingle structure!\n");
return -1;
}
chan = jingle_new(client, p, AST_STATE_DOWN, pak->from->user);
chan = jingle_new(client, p, AST_STATE_DOWN, pak->from->user, NULL);
if (!chan) {
jingle_free_pvt(client, p);
return -1;
@ -1457,7 +1457,7 @@ static int jingle_hangup(struct ast_channel *ast)
}
/*! \brief Part of PBX interface */
static struct ast_channel *jingle_request(const char *request_type, int format, void *data, int *cause)
static struct ast_channel *jingle_request(const char *type, int format, const struct ast_channel *requestor, void *data, int *cause)
{
struct jingle_pvt *p = NULL;
struct jingle *client = NULL;
@ -1495,7 +1495,7 @@ static struct ast_channel *jingle_request(const char *request_type, int format,
ASTOBJ_WRLOCK(client);
p = jingle_alloc(client, to, NULL);
if (p)
chan = jingle_new(client, p, AST_STATE_DOWN, to);
chan = jingle_new(client, p, AST_STATE_DOWN, to, requestor ? requestor->linkedid : NULL);
ASTOBJ_UNLOCK(client);
return chan;

View File

@ -60,7 +60,7 @@ static struct ast_jb_conf g_jb_conf = {
.impl = "",
};
static struct ast_channel *local_request(const char *type, int format, void *data, int *cause);
static struct ast_channel *local_request(const char *type, int format, const struct ast_channel *requestor, void *data, int *cause);
static int local_digit_begin(struct ast_channel *ast, char digit);
static int local_digit_end(struct ast_channel *ast, char digit, unsigned int duration);
static int local_call(struct ast_channel *ast, char *dest, int timeout);
@ -780,7 +780,7 @@ static struct local_pvt *local_alloc(const char *data, int format)
}
/*! \brief Start new local channel */
static struct ast_channel *local_new(struct local_pvt *p, int state)
static struct ast_channel *local_new(struct local_pvt *p, int state, const char *linkedid)
{
struct ast_channel *tmp = NULL, *tmp2 = NULL;
int randnum = ast_random() & 0xffff, fmt = 0;
@ -798,8 +798,8 @@ static struct ast_channel *local_new(struct local_pvt *p, int state)
ama = p->owner->amaflags;
else
ama = 0;
if (!(tmp = ast_channel_alloc(1, state, 0, 0, t, p->exten, p->context, ama, "Local/%s@%s-%04x;1", p->exten, p->context, randnum))
|| !(tmp2 = ast_channel_alloc(1, AST_STATE_RING, 0, 0, t, p->exten, p->context, ama, "Local/%s@%s-%04x;2", p->exten, p->context, randnum))) {
if (!(tmp = ast_channel_alloc(1, state, 0, 0, t, p->exten, p->context, linkedid, ama, "Local/%s@%s-%04x;1", p->exten, p->context, randnum))
|| !(tmp2 = ast_channel_alloc(1, AST_STATE_RING, 0, 0, t, p->exten, p->context, linkedid, ama, "Local/%s@%s-%04x;2", p->exten, p->context, randnum))) {
if (tmp) {
tmp = ast_channel_release(tmp);
}
@ -843,14 +843,14 @@ static struct ast_channel *local_new(struct local_pvt *p, int state)
}
/*! \brief Part of PBX interface */
static struct ast_channel *local_request(const char *type, int format, void *data, int *cause)
static struct ast_channel *local_request(const char *type, int format, const struct ast_channel *requestor, void *data, int *cause)
{
struct local_pvt *p = NULL;
struct ast_channel *chan = NULL;
/* Allocate a new private structure and then Asterisk channel */
if ((p = local_alloc(data, format))) {
if (!(chan = local_new(p, AST_STATE_DOWN))) {
if (!(chan = local_new(p, AST_STATE_DOWN, requestor ? requestor->linkedid : NULL))) {
AST_LIST_LOCK(&locals);
AST_LIST_REMOVE(&locals, p, list);
AST_LIST_UNLOCK(&locals);

View File

@ -418,7 +418,7 @@ static void dump_cmd_queues(struct mgcp_endpoint *p, struct mgcp_subchannel *sub
static char *mgcp_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
static int reload_config(int reload);
static struct ast_channel *mgcp_request(const char *type, int format, void *data, int *cause);
static struct ast_channel *mgcp_request(const char *type, int format, const struct ast_channel *requestor, void *data, int *cause);
static int mgcp_call(struct ast_channel *ast, char *dest, int timeout);
static int mgcp_hangup(struct ast_channel *ast);
static int mgcp_answer(struct ast_channel *ast);
@ -1466,13 +1466,13 @@ static int mgcp_indicate(struct ast_channel *ast, int ind, const void *data, siz
return res;
}
static struct ast_channel *mgcp_new(struct mgcp_subchannel *sub, int state)
static struct ast_channel *mgcp_new(struct mgcp_subchannel *sub, int state, const char *linkedid)
{
struct ast_channel *tmp;
struct mgcp_endpoint *i = sub->parent;
int fmt;
tmp = ast_channel_alloc(1, state, i->cid_num, i->cid_name, i->accountcode, i->exten, i->context, i->amaflags, "MGCP/%s@%s-%d", i->name, i->parent->name, sub->id);
tmp = ast_channel_alloc(1, state, i->cid_num, i->cid_name, linkedid, i->accountcode, i->exten, i->context, i->amaflags, "MGCP/%s@%s-%d", i->name, i->parent->name, sub->id);
if (tmp) {
tmp->tech = &mgcp_tech;
tmp->nativeformats = i->capability;
@ -2967,7 +2967,7 @@ static void handle_hd_hf(struct mgcp_subchannel *sub, char *ev)
#else
transmit_notify_request(sub, "G/rt");
#endif
c = mgcp_new(sub, AST_STATE_RING);
c = mgcp_new(sub, AST_STATE_RING, NULL);
if (!c) {
ast_log(LOG_WARNING, "Unable to start PBX on channel %s@%s\n", p->name, p->parent->name);
transmit_notify_request(sub, "G/cg");
@ -2979,7 +2979,7 @@ static void handle_hd_hf(struct mgcp_subchannel *sub, char *ev)
} else {
transmit_notify_request(sub, "L/dl");
}
c = mgcp_new(sub, AST_STATE_DOWN);
c = mgcp_new(sub, AST_STATE_DOWN, NULL);
if (c) {
if (ast_pthread_create_detached(&t, NULL, mgcp_ss, c)) {
ast_log(LOG_WARNING, "Unable to create switch thread: %s\n", strerror(errno));
@ -3489,7 +3489,7 @@ static int restart_monitor(void)
return 0;
}
static struct ast_channel *mgcp_request(const char *type, int format, void *data, int *cause)
static struct ast_channel *mgcp_request(const char *type, int format, const struct ast_channel *requestor, void *data, int *cause)
{
int oldformat;
struct mgcp_subchannel *sub;
@ -3533,7 +3533,7 @@ static struct ast_channel *mgcp_request(const char *type, int format, void *data
ast_mutex_unlock(&sub->lock);
return NULL;
}
tmpc = mgcp_new(sub->owner ? sub->next : sub, AST_STATE_DOWN);
tmpc = mgcp_new(sub->owner ? sub->next : sub, AST_STATE_DOWN, requestor ? requestor->linkedid : NULL);
ast_mutex_unlock(&sub->lock);
if (!tmpc)
ast_log(LOG_WARNING, "Unable to make channel for '%s'\n", tmp);
@ -3726,7 +3726,7 @@ static struct mgcp_gateway *build_gateway(char *cat, struct ast_variable *v)
strsep(&cntx, "@");
if (ast_strlen_zero(cntx))
cntx = "default";
e->mwi_event_sub = ast_event_subscribe(AST_EVENT_MWI, mwi_event_cb, NULL,
e->mwi_event_sub = ast_event_subscribe(AST_EVENT_MWI, mwi_event_cb, "MGCP MWI subscription", NULL,
AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mbox,
AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, cntx,
AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_EXISTS,

View File

@ -657,7 +657,7 @@ static int *misdn_ports;
static void chan_misdn_log(int level, int port, char *tmpl, ...)
__attribute__((format(printf, 3, 4)));
static struct ast_channel *misdn_new(struct chan_list *cl, int state, char *exten, char *callerid, int format, int port, int c);
static struct ast_channel *misdn_new(struct chan_list *cl, int state, char *exten, char *callerid, int format, const char *linkedid, int port, int c);
static void send_digit_to_chan(struct chan_list *cl, char digit);
static void hangup_chan(struct chan_list *ch);
@ -7468,7 +7468,7 @@ static struct chan_list *init_chan_list(int orig)
return cl;
}
static struct ast_channel *misdn_request(const char *type, int format, void *data, int *cause)
static struct ast_channel *misdn_request(const char *type, int format, const struct ast_channel *requestor, void *data, int *cause)
{
struct ast_channel *ast;
char group[BUFFERSIZE + 1] = "";
@ -7694,7 +7694,7 @@ static struct ast_channel *misdn_request(const char *type, int format, void *dat
}
cl->bc = newbc;
ast = misdn_new(cl, AST_STATE_RESERVED, args.ext, NULL, format, port, channel);
ast = misdn_new(cl, AST_STATE_RESERVED, args.ext, NULL, format, requestor ? requestor->linkedid : NULL, port, channel);
if (!ast) {
ast_log(LOG_ERROR, "Could not create Asterisk channel for Dial(%s)\n", dial_str);
return NULL;
@ -7799,7 +7799,7 @@ static void update_name(struct ast_channel *tmp, int port, int c)
}
}
static struct ast_channel *misdn_new(struct chan_list *chlist, int state, char *exten, char *callerid, int format, int port, int c)
static struct ast_channel *misdn_new(struct chan_list *chlist, int state, char *exten, char *callerid, int format, const char *linkedid, int port, int c)
{
struct ast_channel *tmp;
char *cid_name = 0, *cid_num = 0;
@ -7821,7 +7821,7 @@ static struct ast_channel *misdn_new(struct chan_list *chlist, int state, char
ast_callerid_parse(callerid, &cid_name, &cid_num);
}
tmp = ast_channel_alloc(1, state, cid_num, cid_name, "", exten, "", 0, "%s/%s%d-u%d", misdn_type, c ? "" : "tmp", chan_offset + c, glob_channel++);
tmp = ast_channel_alloc(1, state, cid_num, cid_name, "", exten, "", linkedid, 0, "%s/%s%d-u%d", misdn_type, c ? "" : "tmp", chan_offset + c, glob_channel++);
if (tmp) {
chan_misdn_log(2, 0, " --> * NEW CHANNEL dialed:%s caller:%s\n", exten, callerid);
@ -8436,7 +8436,7 @@ static void misdn_cc_pbx_notify(long record_id, const struct misdn_cc_notify *no
/* Create a channel to notify with */
snprintf(id_str, sizeof(id_str), "%ld", record_id);
chan = ast_channel_alloc(0, AST_STATE_DOWN, id_str, NULL, NULL,
notify->exten, notify->context, 0,
notify->exten, notify->context, NULL, 0,
"mISDN-CC/%ld-%X", record_id, (unsigned) ++sequence);
if (!chan) {
ast_log(LOG_ERROR, "Unable to allocate channel!\n");
@ -9581,7 +9581,7 @@ cb_events(enum event_e event, struct misdn_bchannel *bc, void *user_data)
ch->l3id = bc->l3_id;
ch->addr = bc->addr;
chan = misdn_new(ch, AST_STATE_RESERVED, bc->dialed.number, bc->caller.number, AST_FORMAT_ALAW, bc->port, bc->channel);
chan = misdn_new(ch, AST_STATE_RESERVED, bc->dialed.number, bc->caller.number, AST_FORMAT_ALAW, NULL, bc->port, bc->channel);
if (!chan) {
misdn_lib_send_event(bc,EVENT_RELEASE_COMPLETE);
ast_log(LOG_ERROR, "cb_events: misdn_new failed !\n");

View File

@ -52,7 +52,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
static const char tdesc[] = "Multicast RTP Paging Channel Driver";
/* Forward declarations */
static struct ast_channel *multicast_rtp_request(const char *type, int format, void *data, int *cause);
static struct ast_channel *multicast_rtp_request(const char *type, int format, const struct ast_channel *requestor, void *data, int *cause);
static int multicast_rtp_call(struct ast_channel *ast, char *dest, int timeout);
static int multicast_rtp_hangup(struct ast_channel *ast);
static struct ast_frame *multicast_rtp_read(struct ast_channel *ast);
@ -107,7 +107,7 @@ static int multicast_rtp_hangup(struct ast_channel *ast)
}
/*! \brief Function called when we should prepare to call the destination */
static struct ast_channel *multicast_rtp_request(const char *type, int format, void *data, int *cause)
static struct ast_channel *multicast_rtp_request(const char *type, int format, const struct ast_channel *requestor, void *data, int *cause)
{
char *tmp = ast_strdupa(data), *multicast_type = tmp, *destination, *control;
struct ast_rtp_instance *instance;
@ -140,7 +140,7 @@ static struct ast_channel *multicast_rtp_request(const char *type, int format, v
goto failure;
}
if (!(chan = ast_channel_alloc(1, AST_STATE_DOWN, "", "", "", "", "", 0, "MulticastRTP/%p", instance))) {
if (!(chan = ast_channel_alloc(1, AST_STATE_DOWN, "", "", "", "", "", requestor ? requestor->linkedid : "", 0, "MulticastRTP/%p", instance))) {
ast_rtp_instance_destroy(instance);
goto failure;
}

View File

@ -66,7 +66,7 @@ struct nbs_pvt {
struct ast_module_user *u; /*! for holding a reference to this module */
};
static struct ast_channel *nbs_request(const char *type, int format, void *data, int *cause);
static struct ast_channel *nbs_request(const char *type, int format, const struct ast_channel *requestor, void *data, int *cause);
static int nbs_call(struct ast_channel *ast, char *dest, int timeout);
static int nbs_hangup(struct ast_channel *ast);
static struct ast_frame *nbs_xread(struct ast_channel *ast);
@ -219,10 +219,10 @@ static int nbs_xwrite(struct ast_channel *ast, struct ast_frame *frame)
return 0;
}
static struct ast_channel *nbs_new(struct nbs_pvt *i, int state)
static struct ast_channel *nbs_new(struct nbs_pvt *i, int state, const char *linkedid)
{
struct ast_channel *tmp;
tmp = ast_channel_alloc(1, state, 0, 0, "", "s", context, 0, "NBS/%s", i->stream);
tmp = ast_channel_alloc(1, state, 0, 0, "", "s", context, linkedid, 0, "NBS/%s", i->stream);
if (tmp) {
tmp->tech = &nbs_tech;
ast_channel_set_fd(tmp, 0, nbs_fd(i->nbs));
@ -251,7 +251,7 @@ static struct ast_channel *nbs_new(struct nbs_pvt *i, int state)
}
static struct ast_channel *nbs_request(const char *type, int format, void *data, int *cause)
static struct ast_channel *nbs_request(const char *type, int format, const struct ast_channel *requestor, void *data, int *cause)
{
int oldformat;
struct nbs_pvt *p;
@ -265,7 +265,7 @@ static struct ast_channel *nbs_request(const char *type, int format, void *data,
}
p = nbs_alloc(data);
if (p) {
tmp = nbs_new(p, AST_STATE_DOWN);
tmp = nbs_new(p, AST_STATE_DOWN, requestor ? requestor->linkedid : NULL);
if (!tmp)
nbs_destroy(p);
}

View File

@ -332,7 +332,8 @@ static struct chan_oss_pvt oss_default = {
static int setformat(struct chan_oss_pvt *o, int mode);
static struct ast_channel *oss_request(const char *type, int format, void *data, int *cause);
static struct ast_channel *oss_request(const char *type, int format, const struct ast_channel *requestor,
void *data, int *cause);
static int oss_digit_begin(struct ast_channel *c, char digit);
static int oss_digit_end(struct ast_channel *c, char digit, unsigned int duration);
static int oss_text(struct ast_channel *c, const char *text);
@ -787,11 +788,11 @@ static int oss_indicate(struct ast_channel *c, int cond, const void *data, size_
/*!
* \brief allocate a new channel.
*/
static struct ast_channel *oss_new(struct chan_oss_pvt *o, char *ext, char *ctx, int state)
static struct ast_channel *oss_new(struct chan_oss_pvt *o, char *ext, char *ctx, int state, const char *linkedid)
{
struct ast_channel *c;
c = ast_channel_alloc(1, state, o->cid_num, o->cid_name, "", ext, ctx, 0, "Console/%s", o->device + 5);
c = ast_channel_alloc(1, state, o->cid_num, o->cid_name, "", ext, ctx, linkedid, 0, "Console/%s", o->device + 5);
if (c == NULL)
return NULL;
c->tech = &oss_tech;
@ -830,7 +831,7 @@ static struct ast_channel *oss_new(struct chan_oss_pvt *o, char *ext, char *ctx,
return c;
}
static struct ast_channel *oss_request(const char *type, int format, void *data, int *cause)
static struct ast_channel *oss_request(const char *type, int format, const struct ast_channel *requestor, void *data, int *cause)
{
struct ast_channel *c;
struct chan_oss_pvt *o;
@ -858,7 +859,7 @@ static struct ast_channel *oss_request(const char *type, int format, void *data,
*cause = AST_CAUSE_BUSY;
return NULL;
}
c = oss_new(o, NULL, NULL, AST_STATE_DOWN);
c = oss_new(o, NULL, NULL, AST_STATE_DOWN, requestor ? requestor->linkedid : NULL);
if (c == NULL) {
ast_log(LOG_WARNING, "Unable to create new OSS channel\n");
return NULL;
@ -1117,7 +1118,7 @@ static char *console_dial(struct ast_cli_entry *e, int cmd, struct ast_cli_args
myc = o->ctx;
if (ast_exists_extension(NULL, myc, mye, 1, NULL)) {
o->hookstate = 1;
oss_new(o, mye, myc, AST_STATE_RINGING);
oss_new(o, mye, myc, AST_STATE_RINGING, NULL);
} else
ast_cli(a->fd, "No such extension '%s' in context '%s'\n", mye, myc);
if (s)

View File

@ -150,7 +150,7 @@ static struct phone_pvt {
static char cid_num[AST_MAX_EXTENSION];
static char cid_name[AST_MAX_EXTENSION];
static struct ast_channel *phone_request(const char *type, int format, void *data, int *cause);
static struct ast_channel *phone_request(const char *type, int format, const struct ast_channel *requestor, void *data, int *cause);
static int phone_digit_begin(struct ast_channel *ast, char digit);
static int phone_digit_end(struct ast_channel *ast, char digit, unsigned int duration);
static int phone_call(struct ast_channel *ast, char *dest, int timeout);
@ -844,11 +844,11 @@ static int phone_write(struct ast_channel *ast, struct ast_frame *frame)
return 0;
}
static struct ast_channel *phone_new(struct phone_pvt *i, int state, char *cntx)
static struct ast_channel *phone_new(struct phone_pvt *i, int state, char *cntx, const char *linkedid)
{
struct ast_channel *tmp;
struct phone_codec_data queried_codec;
tmp = ast_channel_alloc(1, state, i->cid_num, i->cid_name, "", i->ext, i->context, 0, "Phone/%s", i->dev + 5);
tmp = ast_channel_alloc(1, state, i->cid_num, i->cid_name, "", i->ext, i->context, linkedid, 0, "Phone/%s", i->dev + 5);
if (tmp) {
tmp->tech = cur_tech;
ast_channel_set_fd(tmp, 0, i->fd);
@ -941,14 +941,14 @@ static void phone_check_exception(struct phone_pvt *i)
!phonee.bits.dtmf_ready) &&
ast_exists_extension(NULL, i->context, i->ext, 1, i->cid_num)) {
/* It's a valid extension in its context, get moving! */
phone_new(i, AST_STATE_RING, i->context);
phone_new(i, AST_STATE_RING, i->context, NULL);
/* No need to restart monitor, we are the monitor */
} else if (!ast_canmatch_extension(NULL, i->context, i->ext, 1, i->cid_num)) {
/* There is nothing in the specified extension that can match anymore.
Try the default */
if (ast_exists_extension(NULL, "default", i->ext, 1, i->cid_num)) {
/* Check the default, too... */
phone_new(i, AST_STATE_RING, "default");
phone_new(i, AST_STATE_RING, "default", NULL);
/* XXX This should probably be justified better XXX */
} else if (!ast_canmatch_extension(NULL, "default", i->ext, 1, i->cid_num)) {
/* It's not a valid extension, give a busy signal */
@ -966,7 +966,7 @@ static void phone_check_exception(struct phone_pvt *i)
offhook = ioctl(i->fd, PHONE_HOOKSTATE);
if (offhook) {
if (i->mode == MODE_IMMEDIATE) {
phone_new(i, AST_STATE_RING, i->context);
phone_new(i, AST_STATE_RING, i->context, NULL);
} else if (i->mode == MODE_DIALTONE) {
ast_module_ref(ast_module_info->self);
/* Reset the extension */
@ -1002,7 +1002,7 @@ static void phone_check_exception(struct phone_pvt *i)
}
if (phonee.bits.pstn_ring) {
ast_verbose("Unit is ringing\n");
phone_new(i, AST_STATE_RING, i->context);
phone_new(i, AST_STATE_RING, i->context, NULL);
}
if (phonee.bits.caller_id)
ast_verbose("We have caller ID\n");
@ -1212,7 +1212,7 @@ static struct phone_pvt *mkif(const char *iface, int mode, int txgain, int rxgai
return tmp;
}
static struct ast_channel *phone_request(const char *type, int format, void *data, int *cause)
static struct ast_channel *phone_request(const char *type, int format, const struct ast_channel *requestor, void *data, int *cause)
{
int oldformat;
struct phone_pvt *p;
@ -1232,7 +1232,7 @@ static struct ast_channel *phone_request(const char *type, int format, void *dat
if (strncmp(name, p->dev + 5, length) == 0 &&
!isalnum(name[length])) {
if (!p->owner) {
tmp = phone_new(p, AST_STATE_DOWN, p->context);
tmp = phone_new(p, AST_STATE_DOWN, p->context, requestor ? requestor->linkedid : NULL);
break;
} else
*cause = AST_CAUSE_BUSY;

View File

@ -272,6 +272,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/event.h"
#include "asterisk/tcptls.h"
#include "asterisk/stun.h"
#include "asterisk/cel.h"
/*** DOCUMENTATION
<application name="SIPDtmfMode" language="en_US">
@ -2339,7 +2340,7 @@ enum t38_action_flag {
in coming releases. */
/*--- PBX interface functions */
static struct ast_channel *sip_request_call(const char *type, int format, void *data, int *cause);
static struct ast_channel *sip_request_call(const char *type, int format, const struct ast_channel *requestor, void *data, int *cause);
static int sip_devicestate(void *data);
static int sip_sendtext(struct ast_channel *ast, const char *text);
static int sip_call(struct ast_channel *ast, char *dest, int timeout);
@ -5096,6 +5097,7 @@ static int create_addr_from_peer(struct sip_pvt *dialog, struct sip_peer *peer)
ast_string_field_set(dialog, mohinterpret, peer->mohinterpret);
ast_string_field_set(dialog, tohost, peer->tohost);
ast_string_field_set(dialog, fullcontact, peer->fullcontact);
ast_string_field_set(dialog, accountcode, peer->accountcode);
ast_string_field_set(dialog, context, peer->context);
ast_string_field_set(dialog, cid_num, peer->cid_num);
ast_string_field_set(dialog, cid_name, peer->cid_name);
@ -6518,7 +6520,7 @@ static int sip_indicate(struct ast_channel *ast, int condition, const void *data
and from handle_request_invite for inbound channels
*/
static struct ast_channel *sip_new(struct sip_pvt *i, int state, const char *title)
static struct ast_channel *sip_new(struct sip_pvt *i, int state, const char *title, const char *linkedid)
{
struct ast_channel *tmp;
struct ast_variable *v = NULL;
@ -6546,7 +6548,7 @@ static struct ast_channel *sip_new(struct sip_pvt *i, int state, const char *tit
sip_pvt_unlock(i);
/* Don't hold a sip pvt lock while we allocate a channel */
tmp = ast_channel_alloc(1, state, i->cid_num, i->cid_name, i->accountcode, i->exten, i->context, i->amaflags, "SIP/%s-%08x", my_name, (int)(long) i);
tmp = ast_channel_alloc(1, state, i->cid_num, i->cid_name, i->accountcode, i->exten, i->context, linkedid, i->amaflags, "SIP/%s-%08x", my_name, (int)(long) i);
}
if (!tmp) {
@ -17936,16 +17938,20 @@ static void handle_response_invite(struct sip_pvt *p, int resp, const char *rest
/* First we ACK */
xmitres = transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, FALSE);
ast_log(LOG_WARNING, "Received response: \"Forbidden\" from '%s'\n", get_header(&p->initreq, "From"));
if (!req->ignore && p->owner)
if (!req->ignore && p->owner) {
ast_set_hangupsource(p->owner, p->owner->name, 0);
ast_queue_control(p->owner, AST_CONTROL_CONGESTION);
}
pvt_set_needdestroy(p, "received 403 response");
sip_alreadygone(p);
break;
case 404: /* Not found */
xmitres = transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, FALSE);
if (p->owner && !req->ignore)
if (p->owner && !req->ignore) {
ast_set_hangupsource(p->owner, p->owner->name, 0);
ast_queue_control(p->owner, AST_CONTROL_CONGESTION);
}
sip_alreadygone(p);
break;
@ -18983,8 +18989,8 @@ static int sip_park(struct ast_channel *chan1, struct ast_channel *chan2, struct
/* Chan2m: The transferer, chan1m: The transferee */
pthread_t th;
transferee = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, chan1->accountcode, chan1->exten, chan1->context, chan1->amaflags, "Parking/%s", chan1->name);
transferer = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, chan2->accountcode, chan2->exten, chan2->context, chan2->amaflags, "SIPPeer/%s", chan2->name);
transferee = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, chan1->accountcode, chan1->exten, chan1->context, chan1->linkedid, chan1->amaflags, "Parking/%s", chan1->name);
transferer = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, chan2->accountcode, chan2->exten, chan2->context, chan2->linkedid, chan2->amaflags, "SIPPeer/%s", chan2->name);
if ((!transferer) || (!transferee)) {
if (transferee) {
transferee->hangupcause = AST_CAUSE_SWITCH_CONGESTION;
@ -20305,7 +20311,7 @@ static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, int
make_our_tag(p->tag, sizeof(p->tag));
/* First invitation - create the channel */
c = sip_new(p, AST_STATE_DOWN, S_OR(p->peername, NULL));
c = sip_new(p, AST_STATE_DOWN, S_OR(p->peername, NULL), NULL);
*recount = 1;
/* Save Record-Route for any later requests we make on this dialogue */
@ -20625,6 +20631,7 @@ static int local_attended_transfer(struct sip_pvt *transferer, struct sip_dual *
struct sip_pvt *targetcall_pvt;
struct ast_party_connected_line connected_to_transferee;
struct ast_party_connected_line connected_to_target;
char transferer_linkedid[32];
/* Check if the call ID of the replaces header does exist locally */
if (!(targetcall_pvt = get_sip_pvt_byid_locked(transferer->refer->replaces_callid, transferer->refer->replaces_callid_totag,
@ -20685,6 +20692,8 @@ static int local_attended_transfer(struct sip_pvt *transferer, struct sip_dual *
ast_set_flag(&transferer->flags[0], SIP_DEFER_BYE_ON_TRANSFER); /* Delay hangup */
ast_copy_string(transferer_linkedid, transferer->owner->linkedid, sizeof(transferer_linkedid));
/* Perform the transfer */
manager_event(EVENT_FLAG_CALL, "Transfer", "TransferMethod: SIP\r\nTransferType: Attended\r\nChannel: %s\r\nUniqueid: %s\r\nSIP-Callid: %s\r\nTargetChannel: %s\r\nTargetUniqueid: %s\r\n",
transferer->owner->name,
@ -20712,6 +20721,14 @@ static int local_attended_transfer(struct sip_pvt *transferer, struct sip_dual *
/* Transfer succeeded! */
const char *xfersound = pbx_builtin_getvar_helper(target.chan1, "ATTENDED_TRANSFER_COMPLETE_SOUND");
while (ast_channel_trylock(target.chan1)) {
sip_pvt_unlock(targetcall_pvt);
sched_yield();
sip_pvt_lock(targetcall_pvt);
}
ast_cel_report_event(target.chan1, AST_CEL_ATTENDEDTRANSFER, NULL, transferer_linkedid, target.chan2);
ast_channel_unlock(target.chan1);
/* Tell transferer that we're done. */
transmit_notify_with_sipfrag(transferer, seqno, "200 OK", TRUE);
append_history(transferer, "Xfer", "Refer succeeded");
@ -21075,6 +21092,17 @@ static int handle_request_refer(struct sip_pvt *p, struct sip_request *req, int
p->refer->refer_to, p->refer->refer_to_context);
/* Success - we have a new channel */
ast_debug(3, "%s transfer succeeded. Telling transferer.\n", p->refer->attendedtransfer? "Attended" : "Blind");
while (ast_channel_trylock(current.chan1)) {
sip_pvt_unlock(p);
sched_yield();
sip_pvt_lock(p);
}
/* XXX - what to we put in CEL 'extra' for attended transfers to external systems? NULL for now */
ast_cel_report_event(current.chan1, p->refer->attendedtransfer? AST_CEL_ATTENDEDTRANSFER : AST_CEL_BLINDTRANSFER, NULL, p->refer->attendedtransfer ? NULL : p->refer->refer_to, current.chan2);
ast_channel_unlock(current.chan1);
transmit_notify_with_sipfrag(p, seqno, "200 Ok", TRUE);
if (p->refer->localtransfer)
p->refer->status = REFER_200OK;
@ -21126,8 +21154,10 @@ static int handle_request_cancel(struct sip_pvt *p, struct sip_request *req)
update_call_counter(p, DEC_CALL_LIMIT);
stop_media_flows(p); /* Immediately stop RTP, VRTP and UDPTL as applicable */
if (p->owner)
if (p->owner) {
ast_set_hangupsource(p->owner, p->owner->name, 0);
ast_queue_hangup(p->owner);
}
else
sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
if (p->initreq.len > 0) {
@ -21392,6 +21422,7 @@ static int handle_request_bye(struct sip_pvt *p, struct sip_request *req)
ast_queue_hangup_with_cause(p->owner, AST_CAUSE_PROTOCOL_ERROR);
}
} else if (p->owner) {
ast_set_hangupsource(p->owner, p->owner->name, 0);
ast_queue_hangup(p->owner);
ast_debug(3, "Received bye, issuing owner hangup\n");
} else {
@ -21421,7 +21452,7 @@ static void add_peer_mwi_subs(struct sip_peer *peer)
struct sip_mailbox *mailbox;
AST_LIST_TRAVERSE(&peer->mailboxes, mailbox, entry) {
mailbox->event_sub = ast_event_subscribe(AST_EVENT_MWI, mwi_event_cb, peer,
mailbox->event_sub = ast_event_subscribe(AST_EVENT_MWI, mwi_event_cb, "SIP mbox event", peer,
AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mailbox->mailbox,
AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, S_OR(mailbox->context, "default"),
AST_EVENT_IE_END);
@ -23248,7 +23279,7 @@ static int sip_devicestate(void *data)
* or SIP/host!dnid
* \endverbatim
*/
static struct ast_channel *sip_request_call(const char *type, int format, void *data, int *cause)
static struct ast_channel *sip_request_call(const char *type, int format, const struct ast_channel *requestor, void *data, int *cause)
{
struct sip_pvt *p;
struct ast_channel *tmpc = NULL;
@ -23394,7 +23425,7 @@ static struct ast_channel *sip_request_call(const char *type, int format, void *
p->prefcodec = oldformat; /* Format for this call */
p->jointcapability = oldformat;
sip_pvt_lock(p);
tmpc = sip_new(p, AST_STATE_DOWN, host); /* Place the call */
tmpc = sip_new(p, AST_STATE_DOWN, host, requestor ? requestor->linkedid : NULL); /* Place the call */
if (sip_cfg.callevents)
manager_event(EVENT_FLAG_SYSTEM, "ChannelUpdate",
"Channel: %s\r\nChanneltype: %s\r\nSIPcallid: %s\r\nSIPfullcontact: %s\r\nPeername: %s\r\n",

View File

@ -1371,9 +1371,9 @@ struct skinnysession {
AST_LIST_ENTRY(skinnysession) list;
};
static struct ast_channel *skinny_request(const char *type, int format, const struct ast_channel *requestor, void *data, int *cause);
static AST_LIST_HEAD_STATIC(sessions, skinnysession);
static struct ast_channel *skinny_request(const char *type, int format, void *data, int *cause);
static int skinny_devicestate(void *data);
static int skinny_call(struct ast_channel *ast, char *dest, int timeout);
static int skinny_hangup(struct ast_channel *ast);
@ -4348,7 +4348,7 @@ static int skinny_indicate(struct ast_channel *ast, int ind, const void *data, s
return 0;
}
static struct ast_channel *skinny_new(struct skinny_line *l, int state)
static struct ast_channel *skinny_new(struct skinny_line *l, int state, const char *linkedid)
{
struct ast_channel *tmp;
struct skinny_subchannel *sub;
@ -4361,7 +4361,7 @@ static struct ast_channel *skinny_new(struct skinny_line *l, int state)
return NULL;
}
tmp = ast_channel_alloc(1, state, l->cid_num, l->cid_name, l->accountcode, l->exten, l->context, l->amaflags, "Skinny/%s@%s-%d", l->name, d->name, callnums);
tmp = ast_channel_alloc(1, state, l->cid_num, l->cid_name, l->accountcode, l->exten, l->context, linkedid, l->amaflags, "Skinny/%s@%s-%d", l->name, d->name, callnums);
if (!tmp) {
ast_log(LOG_WARNING, "Unable to allocate channel structure\n");
return NULL;
@ -4544,7 +4544,7 @@ static int handle_transfer_button(struct skinny_subchannel *sub)
if (!sub->onhold) {
skinny_hold(sub);
}
c = skinny_new(l, AST_STATE_DOWN);
c = skinny_new(l, AST_STATE_DOWN, NULL);
if (c) {
newsub = c->tech_pvt;
/* point the sub and newsub at each other so we know they are related */
@ -4822,7 +4822,7 @@ static int handle_stimulus_message(struct skinny_req *req, struct skinnysession
break;
}
c = skinny_new(l, AST_STATE_DOWN);
c = skinny_new(l, AST_STATE_DOWN, NULL);
if (!c) {
ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
} else {
@ -4860,7 +4860,7 @@ static int handle_stimulus_message(struct skinny_req *req, struct skinnysession
}
if (!sub || !sub->owner)
c = skinny_new(l, AST_STATE_DOWN);
c = skinny_new(l, AST_STATE_DOWN, NULL);
else
c = sub->owner;
@ -4920,7 +4920,7 @@ static int handle_stimulus_message(struct skinny_req *req, struct skinnysession
ast_verb(1, "Received Stimulus: Voicemail(%d/%d)\n", instance, callreference);
if (!sub || !sub->owner) {
c = skinny_new(l, AST_STATE_DOWN);
c = skinny_new(l, AST_STATE_DOWN, NULL);
} else {
c = sub->owner;
}
@ -5005,7 +5005,7 @@ static int handle_stimulus_message(struct skinny_req *req, struct skinnysession
ast_verb(1, "Received Stimulus: Forward All(%d/%d)\n", instance, callreference);
if (!sub || !sub->owner) {
c = skinny_new(l, AST_STATE_DOWN);
c = skinny_new(l, AST_STATE_DOWN, NULL);
} else {
c = sub->owner;
}
@ -5022,7 +5022,7 @@ static int handle_stimulus_message(struct skinny_req *req, struct skinnysession
ast_verb(1, "Received Stimulus: Forward Busy (%d/%d)\n", instance, callreference);
if (!sub || !sub->owner) {
c = skinny_new(l, AST_STATE_DOWN);
c = skinny_new(l, AST_STATE_DOWN, NULL);
} else {
c = sub->owner;
}
@ -5040,7 +5040,7 @@ static int handle_stimulus_message(struct skinny_req *req, struct skinnysession
#if 0 /* Not sure how to handle this yet */
if (!sub || !sub->owner) {
c = skinny_new(l, AST_STATE_DOWN);
c = skinny_new(l, AST_STATE_DOWN, NULL);
} else {
c = sub->owner;
}
@ -5091,7 +5091,7 @@ static int handle_stimulus_message(struct skinny_req *req, struct skinnysession
if (sub && sub->owner) {
ast_debug(1, "Current subchannel [%s] already has owner\n", sub->owner->name);
} else {
c = skinny_new(l, AST_STATE_DOWN);
c = skinny_new(l, AST_STATE_DOWN, NULL);
if (c) {
sub = c->tech_pvt;
l->activesub = sub;
@ -5189,7 +5189,7 @@ static int handle_offhook_message(struct skinny_req *req, struct skinnysession *
if (sub && sub->owner) {
ast_debug(1, "Current sub [%s] already has owner\n", sub->owner->name);
} else {
c = skinny_new(l, AST_STATE_DOWN);
c = skinny_new(l, AST_STATE_DOWN, NULL);
if (c) {
sub = c->tech_pvt;
l->activesub = sub;
@ -5678,7 +5678,7 @@ static int handle_enbloc_call_message(struct skinny_req *req, struct skinnysessi
l = sub->parent;
}
c = skinny_new(l, AST_STATE_DOWN);
c = skinny_new(l, AST_STATE_DOWN, NULL);
if(!c) {
ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
@ -5792,7 +5792,7 @@ static int handle_soft_key_event_message(struct skinny_req *req, struct skinnyse
}
if (!sub || !sub->owner) {
c = skinny_new(l, AST_STATE_DOWN);
c = skinny_new(l, AST_STATE_DOWN, NULL);
} else {
c = sub->owner;
}
@ -5828,7 +5828,7 @@ static int handle_soft_key_event_message(struct skinny_req *req, struct skinnyse
ast_verb(1, "Received Softkey Event: New Call(%d/%d)\n", instance, callreference);
/* New Call ALWAYS gets a new sub-channel */
c = skinny_new(l, AST_STATE_DOWN);
c = skinny_new(l, AST_STATE_DOWN, NULL);
sub = c->tech_pvt;
/* transmit_ringer_mode(d, SKINNY_RING_OFF);
@ -5898,7 +5898,7 @@ static int handle_soft_key_event_message(struct skinny_req *req, struct skinnyse
ast_verb(1, "Received Softkey Event: Forward All(%d/%d)\n", instance, callreference);
if (!sub || !sub->owner) {
c = skinny_new(l, AST_STATE_DOWN);
c = skinny_new(l, AST_STATE_DOWN, NULL);
} else {
c = sub->owner;
}
@ -5916,7 +5916,7 @@ static int handle_soft_key_event_message(struct skinny_req *req, struct skinnyse
ast_verb(1, "Received Softkey Event: Forward Busy (%d/%d)\n", instance, callreference);
if (!sub || !sub->owner) {
c = skinny_new(l, AST_STATE_DOWN);
c = skinny_new(l, AST_STATE_DOWN, NULL);
} else {
c = sub->owner;
}
@ -5935,7 +5935,7 @@ static int handle_soft_key_event_message(struct skinny_req *req, struct skinnyse
#if 0 /* Not sure how to handle this yet */
if (!sub || !sub->owner) {
c = skinny_new(l, AST_STATE_DOWN);
c = skinny_new(l, AST_STATE_DOWN, NULL);
} else {
c = sub->owner;
}
@ -6565,7 +6565,7 @@ static int skinny_devicestate(void *data)
return get_devicestate(l);
}
static struct ast_channel *skinny_request(const char *type, int format, void *data, int *cause)
static struct ast_channel *skinny_request(const char *type, int format, const struct ast_channel *requestor, void *data, int *cause)
{
int oldformat;
@ -6592,7 +6592,7 @@ static struct ast_channel *skinny_request(const char *type, int format, void *da
return NULL;
}
ast_verb(3, "skinny_request(%s)\n", tmp);
tmpc = skinny_new(l, AST_STATE_DOWN);
tmpc = skinny_new(l, AST_STATE_DOWN, requestor ? requestor->linkedid : NULL);
if (!tmpc) {
ast_log(LOG_WARNING, "Unable to make channel for '%s'\n", tmp);
}
@ -7020,7 +7020,7 @@ static struct ast_channel *skinny_request(const char *type, int format, void *da
strsep(&cfg_context, "@");
if (ast_strlen_zero(cfg_context))
cfg_context = "default";
l->mwi_event_sub = ast_event_subscribe(AST_EVENT_MWI, mwi_event_cb, l,
l->mwi_event_sub = ast_event_subscribe(AST_EVENT_MWI, mwi_event_cb, "skinny MWI subsciption", l,
AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, cfg_mailbox,
AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, cfg_context,
AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_EXISTS,

View File

@ -670,13 +670,13 @@ static const char tdesc[] = "UNISTIM Channel Driver";
static const char channel_type[] = "USTM";
/*! Protos */
static struct ast_channel *unistim_new(struct unistim_subchannel *sub, int state);
static struct ast_channel *unistim_new(struct unistim_subchannel *sub, int state, const char *linkedid);
static int load_module(void);
static int reload(void);
static int unload_module(void);
static int reload_config(void);
static void show_main_page(struct unistimsession *pte);
static struct ast_channel *unistim_request(const char *type, int format,
static struct ast_channel *unistim_request(const char *type, int format, const struct ast_channel *requestor,
void *data, int *cause);
static int unistim_call(struct ast_channel *ast, char *dest, int timeout);
static int unistim_hangup(struct ast_channel *ast);
@ -2363,7 +2363,7 @@ static void HandleCallOutgoing(struct unistimsession *s)
return;
}
if (!sub->owner) { /* A call is already in progress ? */
c = unistim_new(sub, AST_STATE_DOWN); /* No, starting a new one */
c = unistim_new(sub, AST_STATE_DOWN, NULL); /* No, starting a new one */
if (c) {
/* Need to start RTP before calling ast_pbx_run */
if (!sub->rtp)
@ -2411,7 +2411,7 @@ static void HandleCallOutgoing(struct unistimsession *s)
}
send_tone(s, 0, 0);
/* Make new channel */
c = unistim_new(p->subs[SUB_THREEWAY], AST_STATE_DOWN);
c = unistim_new(p->subs[SUB_THREEWAY], AST_STATE_DOWN, NULL);
if (!c) {
ast_log(LOG_WARNING, "Cannot allocate new structure on channel %p\n", p);
return;
@ -4422,7 +4422,7 @@ static int unistim_send_mwi_to_peer(struct unistimsession *s, unsigned int tick)
/*--- unistim_new: Initiate a call in the UNISTIM channel */
/* called from unistim_request (calls from the pbx ) */
static struct ast_channel *unistim_new(struct unistim_subchannel *sub, int state)
static struct ast_channel *unistim_new(struct unistim_subchannel *sub, int state, const char *linkedid)
{
struct ast_channel *tmp;
struct unistim_line *l;
@ -4438,7 +4438,7 @@ static struct ast_channel *unistim_new(struct unistim_subchannel *sub, int state
}
l = sub->parent;
tmp = ast_channel_alloc(1, state, l->cid_num, NULL, l->accountcode, l->exten,
l->context, l->amaflags, "%s@%s-%d", l->name, l->parent->name, sub->subtype);
l->context, linkedid, l->amaflags, "%s@%s-%d", l->name, l->parent->name, sub->subtype);
if (unistimdebug)
ast_verb(0, "unistim_new sub=%d (%p) chan=%p\n", sub->subtype, sub, tmp);
if (!tmp) {
@ -4617,7 +4617,7 @@ static int restart_monitor(void)
/*--- unistim_request: PBX interface function ---*/
/* UNISTIM calls initiated by the PBX arrive here */
static struct ast_channel *unistim_request(const char *type, int format, void *data,
static struct ast_channel *unistim_request(const char *type, int format, const struct ast_channel *requestor, void *data,
int *cause)
{
int oldformat;
@ -4660,7 +4660,7 @@ static struct ast_channel *unistim_request(const char *type, int format, void *d
return NULL;
}
sub->parent->capability = format;
tmpc = unistim_new(sub, AST_STATE_DOWN);
tmpc = unistim_new(sub, AST_STATE_DOWN, requestor ? requestor->linkedid : NULL);
if (!tmpc)
ast_log(LOG_WARNING, "Unable to make channel for '%s'\n", tmp);
if (unistimdebug)

View File

@ -666,8 +666,9 @@ static char *usbradio_active; /* the active device */
static int setformat(struct chan_usbradio_pvt *o, int mode);
static struct ast_channel *usbradio_request(const char *type, int format, void *data
, int *cause);
static struct ast_channel *usbradio_request(const char *type, int format,
const struct ast_channel *requestor,
void *data, int *cause);
static int usbradio_digit_begin(struct ast_channel *c, char digit);
static int usbradio_digit_end(struct ast_channel *c, char digit, unsigned int duration);
static int usbradio_text(struct ast_channel *c, const char *text);
@ -2186,11 +2187,11 @@ static int usbradio_indicate(struct ast_channel *c, int cond, const void *data,
/*
* allocate a new channel.
*/
static struct ast_channel *usbradio_new(struct chan_usbradio_pvt *o, char *ext, char *ctx, int state)
static struct ast_channel *usbradio_new(struct chan_usbradio_pvt *o, char *ext, char *ctx, int state, const char *linkedid)
{
struct ast_channel *c;
c = ast_channel_alloc(1, state, o->cid_num, o->cid_name, "", ext, ctx, 0, "Radio/%s", o->name);
c = ast_channel_alloc(1, state, o->cid_num, o->cid_name, "", ext, ctx, linkedid, 0, "Radio/%s", o->name);
if (c == NULL)
return NULL;
c->tech = &usbradio_tech;
@ -2229,7 +2230,7 @@ static struct ast_channel *usbradio_new(struct chan_usbradio_pvt *o, char *ext,
}
/*
*/
static struct ast_channel *usbradio_request(const char *type, int format, void *data, int *cause)
static struct ast_channel *usbradio_request(const char *type, int format, const struct ast_channel *requestor, void *data, int *cause)
{
struct ast_channel *c;
struct chan_usbradio_pvt *o = find_desc(data);
@ -2254,7 +2255,7 @@ static struct ast_channel *usbradio_request(const char *type, int format, void *
*cause = AST_CAUSE_BUSY;
return NULL;
}
c = usbradio_new(o, NULL, NULL, AST_STATE_DOWN);
c = usbradio_new(o, NULL, NULL, AST_STATE_DOWN, requestor ? requestor->linkedid : NULL);
if (c == NULL) {
ast_log(LOG_WARNING, "Unable to create new usb channel\n");
return NULL;

View File

@ -329,9 +329,9 @@ static struct vpb_pvt {
} *iflist = NULL;
static struct ast_channel *vpb_new(struct vpb_pvt *i, enum ast_channel_state state, const char *context);
static struct ast_channel *vpb_new(struct vpb_pvt *i, enum ast_channel_state state, const char *context, const char *linkedid);
static void *do_chanreads(void *pvt);
static struct ast_channel *vpb_request(const char *type, int format, void *data, int *cause);
static struct ast_channel *vpb_request(const char *type, int format, const struct ast_channel *requestor, void *data, int *cause);
static int vpb_digit_begin(struct ast_channel *ast, char digit);
static int vpb_digit_end(struct ast_channel *ast, char digit, unsigned int duration);
static int vpb_call(struct ast_channel *ast, char *dest, int timeout);
@ -1116,7 +1116,7 @@ static inline int monitor_handle_notowned(struct vpb_pvt *p, VPB_EVENT *e)
break;
case VPB_RING:
if (p->mode == MODE_FXO) /* FXO port ring, start * */ {
vpb_new(p, AST_STATE_RING, p->context);
vpb_new(p, AST_STATE_RING, p->context, NULL);
if (UsePolarityCID != 1) {
if (p->callerid_type == 1) {
ast_verb(4, "Using VPB Caller ID\n");
@ -1140,7 +1140,7 @@ static inline int monitor_handle_notowned(struct vpb_pvt *p, VPB_EVENT *e)
case VPB_STATION_OFFHOOK:
if (p->mode == MODE_IMMEDIATE) {
vpb_new(p,AST_STATE_RING, p->context);
vpb_new(p,AST_STATE_RING, p->context, NULL);
} else {
ast_verb(4, "%s: handle_notowned: playing dialtone\n", p->dev);
playtone(p->handle, &Dialtone);
@ -1185,7 +1185,7 @@ static inline int monitor_handle_notowned(struct vpb_pvt *p, VPB_EVENT *e)
if (ast_exists_extension(NULL, p->context, p->ext, 1, p->callerid)){
ast_verb(4, "%s: handle_notowned: DTMF IDD timer out, matching on [%s] in [%s]\n", p->dev, p->ext, p->context);
vpb_new(p, AST_STATE_RING, p->context);
vpb_new(p, AST_STATE_RING, p->context, NULL);
}
} else if (e->data == p->ring_timer_id) {
/* We didnt get another ring in time! */
@ -1261,11 +1261,11 @@ static inline int monitor_handle_notowned(struct vpb_pvt *p, VPB_EVENT *e)
vpb_timer_start(p->dtmfidd_timer);
} else {
ast_verb(4, "%s: handle_notowned: Matched on [%s] in [%s]\n", p->dev, p->ext , p->context);
vpb_new(p, AST_STATE_UP, p->context);
vpb_new(p, AST_STATE_UP, p->context, NULL);
}
} else if (!ast_canmatch_extension(NULL, p->context, p->ext, 1, p->callerid)) {
if (ast_exists_extension(NULL, "default", p->ext, 1, p->callerid)) {
vpb_new(p, AST_STATE_UP, "default");
vpb_new(p, AST_STATE_UP, "default", NULL);
} else if (!ast_canmatch_extension(NULL, "default", p->ext, 1, p->callerid)) {
ast_verb(4, "%s: handle_notowned: can't match anything in %s or default\n", p->dev, p->context);
playtone(p->handle, &Busytone);
@ -2466,7 +2466,7 @@ static void *do_chanreads(void *pvt)
return NULL;
}
static struct ast_channel *vpb_new(struct vpb_pvt *me, enum ast_channel_state state, const char *context)
static struct ast_channel *vpb_new(struct vpb_pvt *me, enum ast_channel_state state, const char *context, const char *linkedid)
{
struct ast_channel *tmp;
char cid_num[256];
@ -2478,7 +2478,7 @@ static struct ast_channel *vpb_new(struct vpb_pvt *me, enum ast_channel_state st
}
ast_verb(4, "%s: New call for context [%s]\n", me->dev, context);
tmp = ast_channel_alloc(1, state, 0, 0, "", me->ext, me->context, 0, "%s", me->dev);
tmp = ast_channel_alloc(1, state, 0, 0, "", me->ext, me->context, linkedid, 0, "%s", me->dev);
if (tmp) {
if (use_ast_ind == 1){
tmp->tech = &vpb_tech_indicate;
@ -2541,7 +2541,7 @@ static struct ast_channel *vpb_new(struct vpb_pvt *me, enum ast_channel_state st
return tmp;
}
static struct ast_channel *vpb_request(const char *type, int format, void *vdata, int *cause)
static struct ast_channel *vpb_request(const char *type, int format, const struct ast_channel *requestor, void *vdata, int *cause)
{
int oldformat;
struct vpb_pvt *p;
@ -2573,13 +2573,13 @@ static struct ast_channel *vpb_request(const char *type, int format, void *vdata
if (group == -1) {
if (strncmp(s, p->dev + 4, sizeof p->dev) == 0) {
if (!p->owner) {
tmp = vpb_new(p, AST_STATE_DOWN, p->context);
tmp = vpb_new(p, AST_STATE_DOWN, p->context, requestor ? requestor->linkedid : NULL);
break;
}
}
} else {
if ((p->group == group) && (!p->owner)) {
tmp = vpb_new(p, AST_STATE_DOWN, p->context);
tmp = vpb_new(p, AST_STATE_DOWN, p->context, requestor ? requestor->linkedid : NULL);
break;
}
}

View File

@ -349,12 +349,12 @@ static int analog_play_tone(struct analog_pvt *p, enum analog_sub sub, enum anal
return -1;
}
static struct ast_channel * analog_new_ast_channel(struct analog_pvt *p, int state, int startpbx, enum analog_sub sub)
static struct ast_channel * analog_new_ast_channel(struct analog_pvt *p, int state, int startpbx, enum analog_sub sub, const struct ast_channel *requestor)
{
struct ast_channel *c;
if (p->calls->new_ast_channel)
c = p->calls->new_ast_channel(p->chan_pvt, state, startpbx, sub);
c = p->calls->new_ast_channel(p->chan_pvt, state, startpbx, sub, requestor);
else
return NULL;
@ -568,7 +568,7 @@ static int analog_update_conf(struct analog_pvt *p)
return 0;
}
struct ast_channel * analog_request(struct analog_pvt *p, int *callwait)
struct ast_channel * analog_request(struct analog_pvt *p, int *callwait, const struct ast_channel *requestor)
{
ast_log(LOG_DEBUG, "%s %d\n", __FUNCTION__, p->channel);
*callwait = (p->owner != NULL);
@ -580,7 +580,7 @@ struct ast_channel * analog_request(struct analog_pvt *p, int *callwait)
}
}
return analog_new_ast_channel(p, AST_STATE_RESERVED, 0, p->owner ? ANALOG_SUB_CALLWAIT : ANALOG_SUB_REAL);
return analog_new_ast_channel(p, AST_STATE_RESERVED, 0, p->owner ? ANALOG_SUB_CALLWAIT : ANALOG_SUB_REAL, requestor);
}
int analog_available(struct analog_pvt *p, int channelmatch, ast_group_t groupmatch, int *busy, int *channelmatched, int *groupmatched)
@ -2623,7 +2623,7 @@ static struct ast_frame *__analog_handle_event(struct analog_pvt *p, struct ast_
goto winkflashdone;
}
/* Make new channel */
chan = analog_new_ast_channel(p, AST_STATE_RESERVED, 0, ANALOG_SUB_THREEWAY);
chan = analog_new_ast_channel(p, AST_STATE_RESERVED, 0, ANALOG_SUB_THREEWAY, NULL);
if (p->dahditrcallerid) {
if (!p->origcid_num)
p->origcid_num = ast_strdup(p->cid_num);
@ -3000,7 +3000,7 @@ int analog_handle_init_event(struct analog_pvt *i, int event)
analog_set_echocanceller(i, 1);
/* The channel is immediately up. Start right away */
res = analog_play_tone(i, ANALOG_SUB_REAL, ANALOG_TONE_RINGTONE);
chan = analog_new_ast_channel(i, AST_STATE_RING, 1, ANALOG_SUB_REAL);
chan = analog_new_ast_channel(i, AST_STATE_RING, 1, ANALOG_SUB_REAL, NULL);
if (!chan) {
ast_log(LOG_WARNING, "Unable to start PBX on channel %d\n", i->channel);
res = analog_play_tone(i, ANALOG_SUB_REAL, ANALOG_TONE_CONGESTION);
@ -3009,7 +3009,7 @@ int analog_handle_init_event(struct analog_pvt *i, int event)
}
} else {
/* Check for callerid, digits, etc */
chan = analog_new_ast_channel(i, AST_STATE_RESERVED, 0, ANALOG_SUB_REAL);
chan = analog_new_ast_channel(i, AST_STATE_RESERVED, 0, ANALOG_SUB_REAL, NULL);
i->ss_astchan = chan;
if (chan) {
if (analog_has_voicemail(i))
@ -3053,9 +3053,9 @@ int analog_handle_init_event(struct analog_pvt *i, int event)
case ANALOG_SIG_SF:
/* Check for callerid, digits, etc */
if (i->cid_start == ANALOG_CID_START_POLARITY_IN) {
chan = analog_new_ast_channel(i, AST_STATE_PRERING, 0, ANALOG_SUB_REAL);
chan = analog_new_ast_channel(i, AST_STATE_PRERING, 0, ANALOG_SUB_REAL, NULL);
} else {
chan = analog_new_ast_channel(i, AST_STATE_RING, 0, ANALOG_SUB_REAL);
chan = analog_new_ast_channel(i, AST_STATE_RING, 0, ANALOG_SUB_REAL, NULL);
}
i->ss_astchan = chan;
if (chan && ast_pthread_create(&threadid, &attr, __analog_ss_thread, i)) {
@ -3153,7 +3153,7 @@ int analog_handle_init_event(struct analog_pvt *i, int event)
ast_verbose(VERBOSE_PREFIX_2 "Starting post polarity "
"CID detection on channel %d\n",
i->channel);
chan = analog_new_ast_channel(i, AST_STATE_PRERING, 0, ANALOG_SUB_REAL);
chan = analog_new_ast_channel(i, AST_STATE_PRERING, 0, ANALOG_SUB_REAL, NULL);
i->ss_astchan = chan;
if (chan && ast_pthread_create(&threadid, &attr, __analog_ss_thread, i)) {
ast_log(LOG_WARNING, "Unable to start simple switch thread on channel %d\n", i->channel);

View File

@ -163,7 +163,7 @@ struct analog_callback {
/*! This function is for swapping of the owners with the underlying subs. Typically it means you need to change the fds
* of the new owner to be the fds of the sub specified, for each of the two subs given */
void (* const swap_subs)(void *pvt, enum analog_sub a, struct ast_channel *new_a_owner, enum analog_sub b, struct ast_channel *new_b_owner);
struct ast_channel * (* const new_ast_channel)(void *pvt, int state, int startpbx, enum analog_sub sub);
struct ast_channel * (* const new_ast_channel)(void *pvt, int state, int startpbx, enum analog_sub sub, const struct ast_channel *requestor);
/* Add the given sub to a conference */
int (* const conf_add)(void *pvt, enum analog_sub sub);
@ -300,7 +300,7 @@ int analog_answer(struct analog_pvt *p, struct ast_channel *ast);
struct ast_frame *analog_exception(struct analog_pvt *p, struct ast_channel *ast);
struct ast_channel * analog_request(struct analog_pvt *p, int *callwait);
struct ast_channel * analog_request(struct analog_pvt *p, int *callwait, const struct ast_channel *requestor);
int analog_available(struct analog_pvt *p, int channelmatch, ast_group_t groupmatch, int *busy, int *channelmatched, int *groupmatched);

View File

@ -143,12 +143,12 @@ static int sig_pri_play_tone(struct sig_pri_chan *p, enum sig_pri_tone tone)
return -1;
}
static struct ast_channel *sig_pri_new_ast_channel(struct sig_pri_chan *p, int state, int startpbx, int ulaw, int transfercapability, char *exten)
static struct ast_channel *sig_pri_new_ast_channel(struct sig_pri_chan *p, int state, int startpbx, int ulaw, int transfercapability, char *exten, const struct ast_channel *requestor)
{
struct ast_channel *c;
if (p->calls->new_ast_channel)
c = p->calls->new_ast_channel(p->chan_pvt, state, startpbx, ulaw, transfercapability, exten);
c = p->calls->new_ast_channel(p->chan_pvt, state, startpbx, ulaw, transfercapability, exten, requestor);
else
return NULL;
@ -160,11 +160,11 @@ static struct ast_channel *sig_pri_new_ast_channel(struct sig_pri_chan *p, int s
return c;
}
struct ast_channel *sig_pri_request(struct sig_pri_chan *p, enum sig_pri_law law)
struct ast_channel *sig_pri_request(struct sig_pri_chan *p, enum sig_pri_law law, const struct ast_channel *requestor)
{
ast_log(LOG_DEBUG, "%s %d\n", __FUNCTION__, p->channel);
return sig_pri_new_ast_channel(p, AST_STATE_RESERVED, 0, law, 0, p->exten);
return sig_pri_new_ast_channel(p, AST_STATE_RESERVED, 0, law, 0, p->exten, requestor);
}
int pri_is_up(struct sig_pri_pri *pri)
@ -704,7 +704,7 @@ static void *pri_dchannel(void *vpri)
if (ast_tvdiff_ms(ast_tvnow(), lastidle) > 1000) {
/* Don't create a new idle call more than once per second */
snprintf(idlen, sizeof(idlen), "%d/%s", pri->pvts[nextidle]->channel, pri->idledial);
idle = sig_pri_request(pri->pvts[nextidle], AST_FORMAT_ULAW);
idle = sig_pri_request(pri->pvts[nextidle], AST_FORMAT_ULAW, NULL);
if (idle) {
pri->pvts[nextidle]->isidlecall = 1;
if (ast_pthread_create_background(&p, NULL, do_idle_thread, idle)) {
@ -1140,7 +1140,7 @@ static void *pri_dchannel(void *vpri)
/* Release the PRI lock while we create the channel */
ast_mutex_unlock(&pri->lock);
c = sig_pri_new_ast_channel(pri->pvts[chanpos], AST_STATE_RESERVED, 0, (e->ring.layer1 = PRI_LAYER_1_ALAW) ? SIG_PRI_ALAW : SIG_PRI_ULAW, e->ring.ctype, pri->pvts[chanpos]->exten);
c = sig_pri_new_ast_channel(pri->pvts[chanpos], AST_STATE_RESERVED, 0, (e->ring.layer1 = PRI_LAYER_1_ALAW) ? SIG_PRI_ALAW : SIG_PRI_ULAW, e->ring.ctype, pri->pvts[chanpos]->exten, NULL);
sig_pri_unlock_private(pri->pvts[chanpos]);
@ -1187,7 +1187,7 @@ static void *pri_dchannel(void *vpri)
} else {
ast_mutex_unlock(&pri->lock);
/* Release PRI lock while we create the channel */
c = sig_pri_new_ast_channel(pri->pvts[chanpos], AST_STATE_RING, 1, (e->ring.layer1 == PRI_LAYER_1_ALAW) ? SIG_PRI_ALAW : SIG_PRI_ULAW, e->ring.ctype, pri->pvts[chanpos]->exten);
c = sig_pri_new_ast_channel(pri->pvts[chanpos], AST_STATE_RING, 1, (e->ring.layer1 == PRI_LAYER_1_ALAW) ? SIG_PRI_ALAW : SIG_PRI_ULAW, e->ring.ctype, pri->pvts[chanpos]->exten, NULL);
if (c) {
char calledtonstr[10];

View File

@ -64,7 +64,7 @@ struct sig_pri_callback {
int (* const set_echocanceller)(void *pvt, int enable);
int (* const train_echocanceller)(void *pvt);
struct ast_channel * (* const new_ast_channel)(void *pvt, int state, int startpbx, enum sig_pri_law law, int transfercapability, char *exten);
struct ast_channel * (* const new_ast_channel)(void *pvt, int state, int startpbx, enum sig_pri_law law, int transfercapability, char *exten, const struct ast_channel *chan);
void (* const fixup_chans)(void *old_chan, void *new_chan);
@ -239,7 +239,7 @@ void pri_event_alarm(struct sig_pri_pri *pri, int index, int before_start_pri);
void pri_event_noalarm(struct sig_pri_pri *pri, int index, int before_start_pri);
struct ast_channel *sig_pri_request(struct sig_pri_chan *p, enum sig_pri_law law);
struct ast_channel *sig_pri_request(struct sig_pri_chan *p, enum sig_pri_law law, const struct ast_channel *requestor);
struct sig_pri_chan *sig_pri_chan_new(void *pvt_data, struct sig_pri_callback *callback, struct sig_pri_pri *pri, int logicalspan, int channo);

103
configs/cel.conf.sample Normal file
View File

@ -0,0 +1,103 @@
;
; Asterisk Channel Event Logging
;
; Channel Event Logging is a mechanism to provide fine-grained event information
; that can be used to generate billing information. Such event information can
; be recorded to databases and files via pluggable backend modules.
;
[general]
; CEL Activation
;
; Use the 'enable' keyword to turn CEL on or off.
;
; Accepted values: yes and no
; Default value: no
;enable=yes
; Application Tracking
;
; Use the 'apps' keyword to specify the list of applications for which you want
; to receive CEL events. This is a comma separated list of Asterisk dialplan
; applications, such as Dial, Queue, and Park.
;
; Accepted values: A comma separated list of Asterisk dialplan applications
; Default value: none
;
; Note: You may also use 'all' which will result in CEL events being reported
; for all Asterisk applications. This may affect Asterisk's performance
; significantly.
apps=dial,park
; Event Tracking
;
; Use the 'events' keyword to specify the list of events which you want to be
; raised when they occur. This is a comma separated list of the values in the
; table below.
;
; Accepted values: A comma separated list of one or more of the following:
; ALL -- Generate entries on all events
; CHAN_START -- The time a channel was created
; CHAN_END -- The time a channel was terminated
; ANSWER -- The time a channel was answered (ie, phone taken off-hook)
; HANGUP -- The time at which a hangup occurred
; CONF_ENTER -- The time a channel was connected into a conference room
; CONF_EXIT -- The time a channel was removed from a conference room
; CONF_START -- The time the first person enters a conference room
; CONF_END -- The time the last person left a conference room (and
; turned out the lights?)
; APP_START -- The time a tracked application was started
; APP_END -- the time a tracked application ended
; PARK_START -- The time a call was parked
; PARK_END -- Unpark event
; BRIDGE_START -- The time a bridge is started
; BRIDGE_END -- The time a bridge is ended
; 3WAY_START -- When a 3-way conference starts (usually via attended xfer)
; 3WAY_END -- When one or all exit a 3-way conference
; BLINDTRANSFER -- When a blind transfer is initiated
; ATTENDEDTRANSFER -- When an attended transfer is initiated
; TRANSFER -- Generic transfer initiated; not used yet...?
; HOOKFLASH -- So far, when a hookflash event occurs on a DAHDI
; interface
; USER_EVENT -- Triggered from the dialplan, and has a name given by the
; user
;
; Default value: none
; (Track no events)
events=APP_START,CHAN_START,CHAN_END,ANSWER,HANGUP,BRIDGE_START,BRIDGE_END
; Date Format
;
; Use the 'dateformat' keyword to specify the date format used when CEL events
; are raised.
;
; Accepted values: A strftime format string (see man strftime)
;
; Example: "%F %T"
; -> This gives the date and time in the format "2009-06-23 17:02:35"
;
; If this option is not specified, the default format is "<seconds>.<microseconds>"
; since epoch. The microseconds field will always be 6 digits in length, meaning it
; may have leading zeros.
;
;dateformat = %F %T
;
; Asterisk Manager Interface (AMI) CEL Backend
;
[manager]
; AMI Backend Activation
;
; Use the 'enable' keyword to turn CEL logging to the Asterisk Manager Interface
; on or off.
;
; Accepted values: yes and no
; Default value: no
;enabled=yes

View File

@ -0,0 +1,106 @@
;
; This configuration defines the connections and tables for which CEL records may
; be populated. Each context specifies a different CEL table to be used.
;
; The columns in the tables should match up word-for-word (case-insensitive)
; to the CEL variables set in the dialplan. The natural advantage to this
; system is that beyond setting up the configuration file to tell you what
; tables to look at, there isn't anything more to do beyond creating the
; columns for the fields that you want, and populating the corresponding
; CEL variables in the dialplan.
;
; Please note that after adding columns to the database, it is necessary to
; reload this module to get the new column names and types read.
;
; Warning: if you specify two contexts with exactly the same connection and
; table names, you will get duplicate records in that table. So be careful.
;
; CEL FIELDS:
; eventtype
; CEL_CHANNEL_START = 1
; CEL_CHANNEL_END = 2
; CEL_HANGUP = 3
; CEL_ANSWER = 4
; CEL_APP_START = 5
; CEL_APP_END = 6
; CEL_BRIDGE_START = 7
; CEL_BRIDGE_END = 8
; CEL_CONF_START = 9
; CEL_CONF_END = 10
; CEL_PARK_START = 11
; CEL_PARK_END = 12
; CEL_BLINDTRANSFER = 13
; CEL_ATTENDEDTRANSFER = 14
; CEL_TRANSFER = 15
; CEL_HOOKFLASH = 16
; CEL_3WAY_START = 17
; CEL_3WAY_END = 18
; CEL_CONF_ENTER = 19
; CEL_CONF_EXIT = 20
; CEL_USER_DEFINED = 21
; CEL_LINKEDID_END = 22
; CEL_BRIDGE_UPDATE = 23
; CEL_PICKUP = 24
; CEL_FORWARD = 25
; eventtime (timeval, includes microseconds)
; userdeftype (set only if eventtype == USER_DEFINED)
; cid_name
; cid_num
; cid_ani
; cid_rdnis
; cid_dnid
; exten
; context
; channame
; appname
; appdata
; accountcode
; peeraccount
; uniqueid
; linkedid
; amaflag (an int)
; userfield
; peer
;[first]
;connection=mysql1
;table=cel
;[second]
;connection=mysql1
;table=extracel
;[third]
;connection=sqlserver
;table=AsteriskCEL
;usegmtime=yes ; defaults to no
;alias src => source
;alias channel => source_channel
;alias dst => dest
;alias dstchannel => dest_channel
;
; Any filter specified MUST match exactly or the CDR will be discarded
;filter accountcode => somename
;filter src => 123
;
; Additionally, we now support setting static values per column. Reason
; for this is to allow different sections to specify different values for
; a certain named column, presumably separated by filters.
;static "Some Special Value" => identifier_code
; On Wednesday 10 September 2008 21:11:16 Tilghman Lesher wrote:
; (this module patterned after the CDR module)
; I thought that the sample cdr_adaptive_odbc.conf was rather clear, but
; apparently not. The point of this module is to allow you log whatever you
; like in terms of the CDR variables. Do you want to log uniqueid? Then simply
; ensure that your table has that column. If you don't want the column, ensure
; that it does not exist in the table structure. If you'd like to call uniqueid
; something else in your table, simply provide an alias in the configuration
; file that maps the standard CDR field name (uniqueid) to whatever column
; name you like.
At the current time, channel variables are not published with the events.
If you wish to store variables, put them in the channel userfield and
extract them from there.

View File

@ -0,0 +1,20 @@
;
; Asterisk Channel Event Logging - Custom CSV Backend
;
; This is the configuration file for the customizable CSV backend for CEL
; logging.
;
; In order to create custom CSV logs for CEL, uncomment the template below
; (Master.csv) and start Asterisk. Once CEL events are generated, a file will
; appear in the following location:
;
; /var/log/asterisk/cel-custom/Master.csv
;
; (Note that /var/log/asterisk is the default and may differ on your system)
;
; You can also create more than one template if desired. All logs will appear
; in the cel-custom directory under your Asterisk logs directory.
;
[mappings]
;Master.csv => "${eventtype}","${eventtime}","${CALLERID(name)}","${CALLERID(num)}","${CALLERID(ANI)}","${CALLERID(RDNIS)}","${CALLERID(DNID)}","${CHANNEL(exten)}","${CHANNEL(context)}","${CHANNEL(channame)}","${CHANNEL(appname)}","${CHANNEL(appdata)}","${CHANNEL(amaflags)}","${CHANNEL(accountcode)}","${CHANNEL(uniqueid)}","${CHANNEL(linkedid)}","${CHANNEL(peer)}","${CHANNEL(userfield)}"

View File

@ -0,0 +1,58 @@
; Sample Asterisk config file for CEL logging to PostgresSQL
;
; CEL field names:
;
; eventtype
; CEL_CHANNEL_START = 1
; CEL_CHANNEL_END = 2
; CEL_HANGUP = 3
; CEL_ANSWER = 4
; CEL_APP_START = 5
; CEL_APP_END = 6
; CEL_BRIDGE_START = 7
; CEL_BRIDGE_END = 8
; CEL_CONF_START = 9
; CEL_CONF_END = 10
; CEL_PARK_START = 11
; CEL_PARK_END = 12
; CEL_BLINDTRANSFER = 13
; CEL_ATTENDEDTRANSFER = 14
; CEL_TRANSFER = 15
; CEL_HOOKFLASH = 16
; CEL_3WAY_START = 17
; CEL_3WAY_END = 18
; CEL_CONF_ENTER = 19
; CEL_CONF_EXIT = 20
; CEL_USER_DEFINED = 21
; CEL_LINKEDID_END = 22
; CEL_BRIDGE_UPDATE = 23
; CEL_PICKUP = 24
; CEL_FORWARD = 25
; eventtime (timeval, includes microseconds)
; userdeftype (set only if eventtype == USER_DEFINED)
; cid_name
; cid_num
; cid_ani
; cid_rdnis
; cid_dnid
; exten
; context
; channame
; appname
; appdata
; accountcode
; peeraccount
; uniqueid
; linkedid
; amaflag (an int)
; userfield
; peer
[global]
;hostname=localhost
;port=5432
;dbname=asterisk
;password=password
;user=postgres
;table=cel ;SQL table where CEL's will be inserted

View File

@ -0,0 +1,7 @@
;
; Mappings for sqlite3 config file
;
;[master] ; currently, only file "master.db" is supported, with only one table at a time.
;table => cel
;columns => eventtype, eventtime, cidname, cidnum, cidani, cidrdnis, ciddnid, context, exten, channame, appname, appdata, amaflags, accountcode, uniqueid, userfield, peer
;values => '${eventtype}','${eventtime}','${CALLERID(name)}','${CALLERID(num)}','${CALLERID(ANI)}','${CALLERID(RDNIS)}','${CALLERID(DNID)}','${CHANNEL(context)}','${CHANNEL(exten)}','${CHANNEL(channame)}','${CHANNEL(appname)}','${CHANNEL(appdata)}','${CHANNEL(amaflags)}','${CHANNEL(accountcode)}','${CHANNEL(uniqueid)}','${CHANNEL(userfield)}','${CHANNEL(peer)}'

View File

@ -0,0 +1,69 @@
;
; Asterisk Channel Event Logging (CEL) - FreeTDS Backend
;
;[global]
; Connection
;
; Use the 'connection' keyword to specify one of the instance names from your
; 'freetds.conf' file. Note that 'freetds.conf' is not an Asterisk
; configuration file, but one specific to the FreeTDS library. See the FreeTDS
; documentation on 'freetds.conf' for more information:
;
; http://www.freetds.org/userguide/freetdsconf.htm
;
; Accepted values: One of the connections specified in freetds.conf
;connection=ConnectionFromFreeTDSConf
; Database Name
;
; The 'dbname' keyword specifies the database name to use when logging CEL
; records.
;
; Accepted values: Any valid database name
;dbname=MalicoHN
; Database Table Name
;
; The 'table' keyword identifies which database table is used to log CEL
; records.
;
; Accepted value: Any valid table name
; Default value: If not specified, a table named 'cel' is assumed
;table=cel
; Credentials
;
; The 'username' and 'password' keywords specify the user credentials that
; Asterisk should use when connecting to the database.
;
; Accepted value: Any valid username and password
;username=mangUsr
;password=
; Language
;
; The 'language' keyword changes the language which are used for error and
; information messages returned by SQL Server. Each database and user has their
; own default value, and this default can be overriden here.
;
; Accepted value: Any language installed on the target SQL Server.
; Default value: Server default
;language=us_english
; Character Set
;
; The 'charset' setting is used to change the character set used when connecting
; to the database server. Each database and database user has their own
; character set setting, and this default can be overriden here.
;
; Accepted value: Any valid character set available on the target server.
; Default value: Server setting
;charset=BIG5

View File

@ -117,6 +117,10 @@ reference purposes.
\input{billing.tex}
\input{cdrdriver.tex}
\chapter{CEL: Channel Event Logging}
\input{cel-doc.tex}
\input{celdriver.tex}
\chapter{Voicemail}
\section{ODBC Storage}
\label{odbcstorage}

958
doc/tex/cel-doc.tex Normal file
View File

@ -0,0 +1,958 @@
\section{Design Goals}
CEL, or Channel Event Logging, has been written with the hopes that it will help
solve some of the problems that were difficult to address in CDR records. Some
difficulties in CDR generation are the fact that the CDR record stores three
events: the "Start" time, the "Answer" time, and the "End" time. Billing time is
usually the difference between "Answer" and "End", and total call duration was
the difference in time from "Start" to "End". The trouble with this direct and
simple approach is the fact that calls can be transferred, put on hold,
conferenced, forwarded, etc. In general, those doing billing applications in
Asterisk find they have to do all sorts of very creative things to overcome the
shortcomings of CDR records, often supplementing the CDR records with AGI
scripts and manager event filters.
The fundamental assumption is that the Channel is the fundamental communication
object in asterisk, which basically provides a communication channel between two
communication ports. It makes sense to have an event system aimed at recording
important events on channels. Each event is attached to a channel, like ANSWER
or HANGUP. Some events are meant to connect two or more channels, like the
BRIDGE\_START event. Some events, like BLINDTRANSFER, are initiated by one
channel, but affect two others. These events use the Peer field, like BRIDGE
would, to point to the target channel.
The design philosophy of CEL is to generate event data that can grouped together
to form a billing record. This may not be a simple task, but we hope to provide
a few different examples that could be used as a basis for those involved in
this effort.
There are definite parallels between Manager events and CEL events, but there
are some differences. Some events that are generated by CEL are not generated
by the Manager interface (yet). CEL is optimized for databases, and Manager
events are not. The focus of CEL is billing. The Manager interface is targeted
to real-time monitoring and control of asterisk.
To give the reader a feel for the complexities involved in billing, please take
note of the following sequence of events:
Remember that 150, 151, and 152 are all Zap extension numbers, and their
respective devices are Zap/50, Zap/51, and Zap/52.
152 dials 151; 151 answers. 152 parks 151; 152 hangs up. 150 picks up the park
(dials 701). 150 and 151 converse. 151 flashes hook; dials 152, talks to 152,
then 151 flashes hook again for 3-way conference. 151 converses with the other
two for a while, then hangs up. 150 and 152 keep conversing, then hang up. 150
hangs up first.(not that it matters).
This sequence of actions will generate the following annotated list of 42 CEL
events:
{\it Note that the actual CEL events below are in CSV format and do not include
the ;;; and text after that which gives a description of what the event
represents.}
\begin{astlisting}
"EV\_CHAN\_START","2007-05-09 12:46:16","fxs.52","152","","","","s","extension","Zap/52-1","","","DOCUMENTATION","","1178736376.3","","" ;;; 152 takes the phone off-hook
"EV\_APP\_START","2007-05-09 12:46:18","fxs.52","152","152","","","151","extension","Zap/52-1","Dial","Zap/51|30|TtWw","DOCUMENTATION","","1178736376.3" ;;; 152 finishes dialing 151
"EV\_CHAN\_START","2007-05-09 12:46:18","fxs.51","151","","","","s","extension","Zap/51-1","","","DOCUMENTATION","","1178736378.4","","" ;;; 151 channel created, starts ringing
{\it (151 is ringing)}
"EV\_ANSWER","2007-05-09 12:46:19","","151","152","","","151","extension","Zap/51-1","AppDial","(Outgoing Line)","DOCUMENTATION","","1178736378.4","","" ;;; 151 answers
"EV\_ANSWER","2007-05-09 12:46:19","fxs.52","152","152","","","151","extension","Zap/52-1","Dial","Zap/51|30|TtWw","DOCUMENTATION","","1178736376.3","","" ;;; so does 152 (???)
"EV\_BRIDGE\_START","2007-05-09 12:46:20","fxs.52","152","152","","","151","extension","Zap/52-1","Dial","Zap/51|30|TtWw","DOCUMENTATION","","1178736376.3","","Zap/51-1" ;;; 152 and 151 are bridged
{\it (151 and 152 are conversing)}
"EV\_BRIDGE\_END","2007-05-09 12:46:25","fxs.52","152","152","","","151","extension","Zap/52-1","Dial","Zap/51|30|TtWw","DOCUMENTATION","","1178736376.3","","" ;;; after 5 seconds, the bridge ends (152 dials \#700?)
"EV\_BRIDGE\_START","2007-05-09 12:46:25","fxs.52","152","152","","","151","extension","Zap/52-1","Dial","Zap/51|30|TtWw","DOCUMENTATION","","1178736376.3","","Zap/51-1" ;;; extraneous 0-second bridge?
"EV\_BRIDGE\_END","2007-05-09 12:46:25","fxs.52","152","152","","","151","extension","Zap/52-1","Dial","Zap/51|30|TtWw","DOCUMENTATION","","1178736376.3","","" ;;;
"EV\_PARK\_START","2007-05-09 12:46:27","","151","152","","","","extension","Zap/51-1","Parked Call","","DOCUMENTATION","","1178736378.4","","" ;;; 151 is parked
"EV\_HANGUP","2007-05-09 12:46:29","fxs.52","152","152","","","h","extension","Zap/52-1","","","DOCUMENTATION","","1178736376.3" ,"","" ;;; 152 hangs up 2 sec later
"EV\_CHAN\_END","2007-05-09 12:46:29","fxs.52","152","152","","","h","extension","Zap/52-1","","","DOCUMENTATION","","1178736376.3","","" ;;; 152's channel goes away
{\it (151 is parked and listening to MOH! now, 150 picks up, and dials 701)}
"EV\_CHAN\_START","2007-05-09 12:47:08","fxs.50","150","","","","s","extension","Zap/50-1","","","DOCUMENTATION","","1178736428.5","","" ;;; 150 picks up the phone, dials 701
"EV\_PARK\_END","2007-05-09 12:47:11","","151","152","","","","extension","Zap/51-1","Parked Call","","DOCUMENTATION","","1178736378.4","","" ;;; 151's park comes to end
"EV\_ANSWER","2007-05-09 12:47:11","fxs.50","150","150","","","701","extension","Zap/50-1","ParkedCall","701","DOCUMENTATION","","1178736428.5","","" ;;; 150 gets answer (twice)
"EV\_ANSWER","2007-05-09 12:47:12","fxs.50","150","150","","","701","extension","Zap/50-1","ParkedCall","701","DOCUMENTATION","","1178736428.5","","" ;;;
"EV\_BRIDGE\_START","2007-05-09 12:47:12","fxs.50","150","150","","","701","extension","Zap/50-1","ParkedCall","701","DOCUMENTATION","","1178736428.5","","Zap/51-1" ;;; bridge begins between 150 and recently parked 151
{\it (150 and 151 are conversing, then 151 hits flash)}
"EV\_CHAN\_START","2007-05-09 12:47:51","fxs.51","151","","","","s","extension","Zap/51-2","","","DOCUMENTATION","","1178736471.6","","" ;;; 39 seconds later, 51-2 channel is created. (151 flashes hook)
"EV\_HOOKFLASH","2007-05-09 12:47:51","","151","152","","","","extension","Zap/51-1","Bridged Call","Zap/50-1","DOCUMENTATION","","1178736378.4","","Zap/51-2" ;;; a marker to record that 151 flashed the hook
"EV\_BRIDGE\_END","2007-05-09 12:47:51","fxs.50","150","150","","","701","extension","Zap/50-1","ParkedCall","701","DOCUMENTATION","","1178736428.5","","Zap/51-1" ;;; bridge ends between 150 and 151
"EV\_BRIDGE\_START","2007-05-09 12:47:51","fxs.50","150","150","","","701","extension","Zap/50-1","ParkedCall","701","DOCUMENTATION","","1178736428.5","","Zap/51-1" ;;; 0-second bridge from 150 to ? 150 gets no sound at all
"EV\_BRIDGE\_END","2007-05-09 12:47:51","fxs.50","150","150","","","701","extension","Zap/50-1","ParkedCall","701","DOCUMENTATION","","1178736428.5","","Zap/51-1" ;;;
"EV\_BRIDGE\_START","2007-05-09 12:47:51","fxs.50","150","150","","","701","extension","Zap/50-1","ParkedCall","701","DOCUMENTATION","","1178736428.5","","Zap/51-1" ;;; bridge start on 150
{\it (151 has dialtone after hitting flash; dials 152)}
"EV\_APP\_START","2007-05-09 12:47:55","fxs.51","151","151","","","152","extension","Zap/51-2","Dial","Zap/52|30|TtWw","DOCUMENTATION","","1178736471.6","","" ;;; 151-2 dials 152 after 4 seconds
"EV\_CHAN\_START","2007-05-09 12:47:55","fxs.52","152","","","","s","extension","Zap/52-1","","","DOCUMENTATION","","1178736475.7" ,"","" ;;; 152 channel created to ring 152.
{\it (152 ringing)}
"EV\_ANSWER","2007-05-09 12:47:58","","152","151","","","152","extension","Zap/52-1","AppDial","(Outgoing Line)","DOCUMENTATION","","1178736475.7","","" ;;; 3 seconds later, 152 answers
"EV\_ANSWER","2007-05-09 12:47:58","fxs.51","151","151","","","152","extension","Zap/51-2","Dial","Zap/52|30|TtWw","DOCUMENTATION","","1178736471.6","","" ;;; ... and 151-2 also answers
"EV\_BRIDGE\_START","2007-05-09 12:47:59","fxs.51","151","151","","","152","extension","Zap/51-2","Dial","Zap/52|30|TtWw","DOCUMENTATION","","1178736471.6","","Zap/51-1" ;;; 1 second later, bridge formed betw. 151-2 and 151
{\it (152 answers, 151 and 152 convering; 150 is listening to silence; 151 hits flash again... to start a 3way)}
"EV\_3WAY\_START","2007-05-09 12:48:58","","151","152","","","","extension","Zap/51-1","Bridged Call","Zap/50-1","DOCUMENTATION","","1178736378.4","","Zap/51-2" ;;; another hook-flash to begin a 3-way conference
"EV\_BRIDGE\_END","2007-05-09 12:48:58","fxs.50","150","150","","","701","extension","Zap/50-1","ParkedCall","701","DOCUMENTATION","","1178736428.5","","Zap/51-1" ;;; -- almost 1 minute later, the bridge ends (151 flashes hook again)
"EV\_BRIDGE\_START","2007-05-09 12:48:58","fxs.50","150","150","","","701","extension","Zap/50-1","ParkedCall","701","DOCUMENTATION","","1178736428.5","","Zap/51-1" ;;; 0-second bridge at 150. (3 way conf formed)
"EV\_BRIDGE\_END","2007-05-09 12:48:58","fxs.50","150","150","","","701","extension","Zap/50-1","ParkedCall","701","DOCUMENTATION","","1178736428.5","","Zap/51-1" ;;;
"EV\_BRIDGE\_START","2007-05-09 12:48:58","fxs.50","150","150","","","701","extension","Zap/50-1","ParkedCall","701","DOCUMENTATION","","1178736428.5","","Zap/51-1" ;;; bridge starts for 150
{\it (3way now, then 151 hangs up.)}
"EV\_BRIDGE\_END","2007-05-09 12:49:26","fxs.50","150","150","","","701","extension","Zap/50-1","ParkedCall","701","DOCUMENTATION","","1178736428.5","","Zap/51-1" ;;; 28 seconds later, bridge ends
"EV\_HANGUP","2007-05-09 12:49:26","","151","152","","","","extension","Zap/51-1","Bridged Call","Zap/50-1","DOCUMENTATION","","1178736378.4","","" ;;; 151 hangs up, leaves 150 and 152 connected
"EV\_CHAN\_END","2007-05-09 12:49:26","","151","152","","","","extension","Zap/51-1","Bridged Call","Zap/50-1","DOCUMENTATION","","1178736378.4","","" ;;; 151 channel ends
"EV\_CHAN\_END","2007-05-09 12:49:26","fxs.51","151","151","","","h","extension","Zap/51-2<ZOMBIE>","","","DOCUMENTATION","","1178736428.5","","" ;;; 152-2 channel ends (zombie)
{\it (just 150 and 152 now)}
"EV\_BRIDGE\_END","2007-05-09 12:50:13","fxs.50","150","150","","","152","extension","Zap/50-1","Dial","Zap/52|30|TtWw","DOCUMENTATION","","1178736471.6","","" ;;; 47 sec later, the bridge from 150 to 152 ends
"EV\_HANGUP","2007-05-09 12:50:13","","152","151","","","","extension","Zap/52-1","Bridged Call","Zap/50-1","DOCUMENTATION","","1178736475.7","","" ;;; 152 hangs up
"EV\_CHAN\_END","2007-05-09 12:50:13","","152","151","","","","extension","Zap/52-1","Bridged Call","Zap/50-1","DOCUMENTATION","","1178736475.7","","" ;;; 152 channel ends
"EV\_HANGUP","2007-05-09 12:50:13","fxs.50","150","150","","","h","extension","Zap/50-1","","","DOCUMENTATION","","1178736471.6","","" ;;; 150 hangs up
"EV\_CHAN\_END","2007-05-09 12:50:13","fxs.50","150","150","","","h","extension","Zap/50-1","","","DOCUMENTATION","","1178736471.6","","" ;;; 150 ends
\end{astlisting}
In terms of Manager events, the above Events correspond to the following 80
Manager events:
\begin{astlisting}
\begin{verbatim}
Event: Newchannel
Privilege: call,all
Channel: Zap/52-1
State: Rsrvd
CallerIDNum: 152
CallerIDName: fxs.52
Uniqueid: 1178801102.5
Event: Newcallerid
Privilege: call,all
Channel: Zap/52-1
CallerIDNum: 152
CallerIDName: fxs.52
Uniqueid: 1178801102.5
CID-CallingPres: 0 (Presentation Allowed, Not Screened)
Event: Newcallerid
Privilege: call,all
Channel: Zap/52-1
CallerIDNum: 152
CallerIDName: fxs.52
Uniqueid: 1178801102.5
CID-CallingPres: 0 (Presentation Allowed, Not Screened)
Event: Newstate
Privilege: call,all
Channel: Zap/52-1
State: Ring
CallerIDNum: 152
CallerIDName: fxs.52
Uniqueid: 1178801102.5
Event: Newexten
Privilege: call,all
Channel: Zap/52-1
Context: extension
Extension: 151
Priority: 1
Application: Set
AppData: CDR(myvar)=zingo
Uniqueid: 1178801102.5
Event: Newexten
Privilege: call,all
Channel: Zap/52-1
Context: extension
Extension: 151
Priority: 2
Application: Dial
AppData: Zap/51|30|TtWw
Uniqueid: 1178801102.5
Event: Newchannel
Privilege: call,all
Channel: Zap/51-1
State: Rsrvd
CallerIDNum: 151
CallerIDName: fxs.51
Uniqueid: 1178801108.6
Event: Newstate
Privilege: call,all
Channel: Zap/51-1
State: Ringing
CallerIDNum: 152
CallerIDName: fxs.52
Uniqueid: 1178801108.6
Event: Dial
Privilege: call,all
SubEvent: Begin
Source: Zap/52-1
Destination: Zap/51-1
CallerIDNum: 152
CallerIDName: fxs.52
SrcUniqueID: 1178801102.5
DestUniqueID: 1178801108.6
Event: Newcallerid
Privilege: call,all
Channel: Zap/51-1
CallerIDNum: 151
CallerIDName: <Unknown>
Uniqueid: 1178801108.6
CID-CallingPres: 0 (Presentation Allowed, Not Screened)
Event: Newstate
Privilege: call,all
Channel: Zap/52-1
State: Ringing
CallerIDNum: 152
CallerIDName: fxs.52
Uniqueid: 1178801102.5
Event: Newstate
Privilege: call,all
Channel: Zap/51-1
State: Up
CallerIDNum: 151
CallerIDName: <unknown>
Uniqueid: 1178801108.6
Event: Newstate
Privilege: call,all
Channel: Zap/52-1
State: Up
CallerIDNum: 152
CallerIDName: fxs.52
Uniqueid: 1178801102.5
Event: Link
Privilege: call,all
Channel1: Zap/52-1
Channel2: Zap/51-1
Uniqueid1: 1178801102.5
Uniqueid2: 1178801108.6
CallerID1: 152
CallerID2: 151
Event: Unlink
Privilege: call,all
Channel1: Zap/52-1
Channel2: Zap/51-1
Uniqueid1: 1178801102.5
Uniqueid2: 1178801108.6
CallerID1: 152
CallerID2: 151
Event: Link
Privilege: call,all
Channel1: Zap/52-1
Channel2: Zap/51-1
Uniqueid1: 1178801102.5
Uniqueid2: 1178801108.6
CallerID1: 152
CallerID2: 151
Event: Unlink
Privilege: call,all
Channel1: Zap/52-1
Channel2: Zap/51-1
Uniqueid1: 1178801102.5
Uniqueid2: 1178801108.6
CallerID1: 152
CallerID2: 151
Event: ParkedCall
Privilege: call,all
Exten: 701
Channel: Zap/51-1
From: Zap/52-1
Timeout: 45
CallerIDNum: 151
CallerIDName: <unknown>
Event: Dial
Privilege: call,all
SubEvent: End
Channel: Zap/52-1
DialStatus: ANSWER
Event: Newexten
Privilege: call,all
Channel: Zap/52-1
Context: extension
Extension: h
Priority: 1
Application: Goto
AppData: label1
Uniqueid: 1178801102.5
Event: Newexten
Privilege: call,all
Channel: Zap/52-1
Context: extension
Extension: h
Priority: 4
Application: Goto
AppData: label2
Uniqueid: 1178801102.5
Event: Newexten
Privilege: call,all
Channel: Zap/52-1
Context: extension
Extension: h
Priority: 2
Application: NoOp
AppData: In Hangup! myvar is zingo and accountcode is billsec is 26 and duration is 40 and end is 2007-05-10 06:45:42.
Uniqueid: 1178801102.5
Event: Newexten
Privilege: call,all
Channel: Zap/52-1
Context: extension
Extension: h
Priority: 3
Application: Goto
AppData: label3
Uniqueid: 1178801102.5
Event: Newexten
Privilege: call,all
Channel: Zap/52-1
Context: extension
Extension: h
Priority: 5
Application: NoOp
AppData: More Hangup message after hopping around"
Uniqueid: 1178801102.5
Event: Hangup
Privilege: call,all
Channel: Zap/52-1
Uniqueid: 1178801102.5
Cause: 16
Cause-txt: Normal Clearing
Event: Newchannel
Privilege: call,all
Channel: Zap/50-1
State: Rsrvd
CallerIDNum: 150
CallerIDName: fxs.50
Uniqueid: 1178801162.7
Event: Newcallerid
Privilege: call,all
Channel: Zap/50-1
CallerIDNum: 150
CallerIDName: fxs.50
Uniqueid: 1178801162.7
CID-CallingPres: 0 (Presentation Allowed, Not Screened)
Event: Newcallerid
Privilege: call,all
Channel: Zap/50-1
CallerIDNum: 150
CallerIDName: fxs.50
Uniqueid: 1178801162.7
CID-CallingPres: 0 (Presentation Allowed, Not Screened)
Event: Newstate
Privilege: call,all
Channel: Zap/50-1
State: Ring
CallerIDNum: 150
CallerIDName: fxs.50
Uniqueid: 1178801162.7
Event: Newexten
Privilege: call,all
Channel: Zap/50-1
Context: extension
Extension: 701
Priority: 1
Application: ParkedCall
AppData: 701
Uniqueid: 1178801162.7
Event: UnParkedCall
Privilege: call,all
Exten: 701
Channel: Zap/51-1
From: Zap/50-1
CallerIDNum: 151
CallerIDName: <unknown>
Event: Newstate
Privilege: call,all
Channel: Zap/50-1
State: Up
CallerIDNum: 150
CallerIDName: fxs.50
Uniqueid: 1178801162.7
Event: Link
Privilege: call,all
Channel1: Zap/50-1
Channel2: Zap/51-1
Uniqueid1: 1178801162.7
Uniqueid2: 1178801108.6
CallerID1: 150
CallerID2: 151
Event: Newchannel
Privilege: call,all
Channel: Zap/51-2
State: Rsrvd
CallerIDNum: 151
CallerIDName: fxs.51
Uniqueid: 1178801218.8
Event: Unlink
Privilege: call,all
Channel1: Zap/50-1
Channel2: Zap/51-1
Uniqueid1: 1178801162.7
Uniqueid2: 1178801108.6
CallerID1: 150
CallerID2: 151
Event: Link
Privilege: call,all
Channel1: Zap/50-1
Channel2: Zap/51-1
Uniqueid1: 1178801162.7
Uniqueid2: 1178801108.6
CallerID1: 150
CallerID2: 151
Event: Unlink
Privilege: call,all
Channel1: Zap/50-1
Channel2: Zap/51-1
Uniqueid1: 1178801162.7
Uniqueid2: 1178801108.6
CallerID1: 150
CallerID2: 151
Event: Link
Privilege: call,all
Channel1: Zap/50-1
Channel2: Zap/51-1
Uniqueid1: 1178801162.7
Uniqueid2: 1178801108.6
CallerID1: 150
CallerID2: 151
Event: Newcallerid
Privilege: call,all
Channel: Zap/51-2
CallerIDNum: 151
CallerIDName: fxs.51
Uniqueid: 1178801218.8
CID-CallingPres: 0 (Presentation Allowed, Not Screened)
Event: Newcallerid
Privilege: call,all
Channel: Zap/51-2
CallerIDNum: 151
CallerIDName: fxs.51
Uniqueid: 1178801218.8
CID-CallingPres: 0 (Presentation Allowed, Not Screened)
Event: Newstate
Privilege: call,all
Channel: Zap/51-2
State: Ring
CallerIDNum: 151
CallerIDName: fxs.51
Uniqueid: 1178801218.8
Event: Newexten
Privilege: call,all
Channel: Zap/51-2
Context: extension
Extension: 152
Priority: 1
Application: Set
AppData: CDR(myvar)=zingo
Uniqueid: 1178801218.8
Event: Newexten
Privilege: call,all
Channel: Zap/51-2
Context: extension
Extension: 152
Priority: 2
Application: Dial
AppData: Zap/52|30|TtWw
Uniqueid: 1178801218.8
Event: Newchannel
Privilege: call,all
Channel: Zap/52-1
State: Rsrvd
CallerIDNum: 152
CallerIDName: fxs.52
Uniqueid: 1178801223.9
Event: Newstate
Privilege: call,all
Channel: Zap/52-1
State: Ringing
CallerIDNum: 151
CallerIDName: fxs.51
Uniqueid: 1178801223.9
Event: Dial
Privilege: call,all
SubEvent: Begin
Source: Zap/51-2
Destination: Zap/52-1
CallerIDNum: 151
CallerIDName: fxs.51
SrcUniqueID: 1178801218.8
DestUniqueID: 1178801223.9
Event: Newcallerid
Privilege: call,all
Channel: Zap/52-1
CallerIDNum: 152
CallerIDName: <Unknown>
Uniqueid: 1178801223.9
CID-CallingPres: 0 (Presentation Allowed, Not Screened)
Event: Newstate
Privilege: call,all
Channel: Zap/51-2
State: Ringing
CallerIDNum: 151
CallerIDName: fxs.51
Uniqueid: 1178801218.8
Event: Newstate
Privilege: call,all
Channel: Zap/52-1
State: Up
CallerIDNum: 152
CallerIDName: <unknown>
Uniqueid: 1178801223.9
Event: Newstate
Privilege: call,all
Channel: Zap/51-2
State: Up
CallerIDNum: 151
CallerIDName: fxs.51
Uniqueid: 1178801218.8
Event: Link
Privilege: call,all
Channel1: Zap/51-2
Channel2: Zap/52-1
Uniqueid1: 1178801218.8
Uniqueid2: 1178801223.9
CallerID1: 151
CallerID2: 152
Event: Unlink
Privilege: call,all
Channel1: Zap/50-1
Channel2: Zap/51-1
Uniqueid1: 1178801162.7
Uniqueid2: 1178801108.6
CallerID1: 150
CallerID2: 151
Event: Link
Privilege: call,all
Channel1: Zap/50-1
Channel2: Zap/51-1
Uniqueid1: 1178801162.7
Uniqueid2: 1178801108.6
CallerID1: 150
CallerID2: 151
Event: Unlink
Privilege: call,all
Channel1: Zap/50-1
Channel2: Zap/51-1
Uniqueid1: 1178801162.7
Uniqueid2: 1178801108.6
CallerID1: 150
CallerID2: 151
Event: Link
Privilege: call,all
Channel1: Zap/50-1
Channel2: Zap/51-1
Uniqueid1: 1178801162.7
Uniqueid2: 1178801108.6
CallerID1: 150
CallerID2: 151
Event: Unlink
Privilege: call,all
Channel1: Zap/50-1
Channel2: Zap/51-1
Uniqueid1: 1178801162.7
Uniqueid2: 1178801108.6
CallerID1: 150
CallerID2: 151
Event: Hangup
Privilege: call,all
Channel: Zap/51-1
Uniqueid: 1178801108.6
Cause: 16
Cause-txt: Normal Clearing
Event: Newexten
Privilege: call,all
Channel: Zap/50-1
Context: extension
Extension: h
Priority: 1
Application: Goto
AppData: label1
Uniqueid: 1178801162.7
Event: Newexten
Privilege: call,all
Channel: Zap/50-1
Context: extension
Extension: h
Priority: 4
Application: Goto
AppData: label2
Uniqueid: 1178801162.7
Event: Newexten
Privilege: call,all
Channel: Zap/50-1
Context: extension
Extension: h
Priority: 2
Application: NoOp
AppData: In Hangup! myvar is and accountcode is billsec is 0 and duration is 0 and end is 2007-05-10 06:48:37.
Uniqueid: 1178801162.7
Event: Newexten
Privilege: call,all
Channel: Zap/50-1
Context: extension
Extension: h
Priority: 3
Application: Goto
AppData: label3
Uniqueid: 1178801162.7
Event: Newexten
Privilege: call,all
Channel: Zap/50-1
Context: extension
Extension: h
Priority: 5
Application: NoOp
AppData: More Hangup message after hopping around"
Uniqueid: 1178801162.7
Event: Masquerade
Privilege: call,all
Clone: Zap/50-1
CloneState: Up
Original: Zap/51-2
OriginalState: Up
Event: Rename
Privilege: call,all
Oldname: Zap/50-1
Newname: Zap/50-1<MASQ>
Uniqueid: 1178801162.7
Event: Rename
Privilege: call,all
Oldname: Zap/51-2
Newname: Zap/50-1
Uniqueid: 1178801218.8
Event: Rename
Privilege: call,all
Oldname: Zap/50-1<MASQ>
Newname: Zap/51-2<ZOMBIE>
Uniqueid: 1178801162.7
Event: Hangup
Privilege: call,all
Channel: Zap/51-2<ZOMBIE>
Uniqueid: 1178801162.7
Cause: 0
Cause-txt: Unknown
Event: Unlink
Privilege: call,all
Channel1: Zap/50-1
Channel2: Zap/52-1
Uniqueid1: 1178801218.8
Uniqueid2: 1178801223.9
CallerID1: 150
CallerID2: 152
Event: Hangup
Privilege: call,all
Channel: Zap/52-1
Uniqueid: 1178801223.9
Cause: 16
Cause-txt: Normal Clearing
Event: Dial
Privilege: call,all
SubEvent: End
Channel: Zap/50-1
DialStatus: ANSWER
Event: Newexten
Privilege: call,all
Channel: Zap/50-1
Context: extension
Extension: h
Priority: 1
Application: Goto
AppData: label1
Uniqueid: 1178801218.8
Event: Newexten
Privilege: call,all
Channel: Zap/50-1
Context: extension
Extension: h
Priority: 4
Application: Goto
AppData: label2
Uniqueid: 1178801218.8
Event: Newexten
Privilege: call,all
Channel: Zap/50-1
Context: extension
Extension: h
Priority: 2
Application: NoOp
AppData: In Hangup! myvar is and accountcode is billsec is 90 and duration is 94 and end is 2007-05-10 06:48:37.
Uniqueid: 1178801218.8
Event: Newexten
Privilege: call,all
Channel: Zap/50-1
Context: extension
Extension: h
Priority: 3
Application: Goto
AppData: label3
Uniqueid: 1178801218.8
Event: Newexten
Privilege: call,all
Channel: Zap/50-1
Context: extension
Extension: h
Priority: 5
Application: NoOp
AppData: More Hangup message after hopping around"
Uniqueid: 1178801218.8
Event: Hangup
Privilege: call,all
Channel: Zap/50-1
Uniqueid: 1178801218.8
Cause: 16
Cause-txt: Normal Clearing
\end{verbatim}
\end{astlisting}
And, humorously enough, the above 80 manager events, or 42 CEL events,
correspond to the following two CDR records (at the moment!):
\begin{astlisting}
""fxs.52" <152>","152","h","extension","Zap/52-1","Zap/51-1","NoOp","More Hangup message after hopping around"","2007-05-09 17:35:56","2007-05-09 17:36:20","2007-05-09 17:36:36","40","16","ANSWERED","DOCUMENTATION","","1178753756.0",""
""fxs.50" <150>","150","152","extension","Zap/50-1","Zap/51-1","NoOp","More Hangup message after hopping around"","2007-05-09 17:37:59","2007-05-09 17:38:06","2007-05-09 17:39:11","72","65","ANSWERED","DOCUMENTATION","","1178753871.3",""
\end{astlisting}
\section{Events \& Fields}
While CDRs and the Manager are basically both event tracking mechanisms, CEL
tries to track only those events that might pertain to billing issues.
See table~\ref{event_table} for a list of events raised by CEL and
table~\ref{field_table} for the list of fields passed for each CEL event.
\begin{table}[h]
\begin{tabular}{ | l | p{10cm} | }
\hline
Event & Description \\ \hline \hline
CHAN\_START & The time a channel was created \\ \hline
CHAN\_END & The time a channel was terminated \\ \hline
ANSWER & The time a channel was answered (ie, phone taken off-hook, etc) \\ \hline
HANGUP & The time at which a hangup occurred. \\ \hline
CONF\_ENTER & The time a channel was connected into a conference room \\ \hline
CONF\_EXIT & The time a channel was removed from a conference room \\ \hline
CONF\_START & The time the first person enters a conference \\ \hline
CONF\_END & The time the last person left a conf (and turned out the lights?) \\ \hline
APP\_START & The time a tracked application was started \\ \hline
APP\_END & the time a tracked application ended \\ \hline
PARK\_START & The time a call was parked \\ \hline
PARK\_END & unpark event \\ \hline
BRIDGE\_START & The time a bridge is started \\ \hline
BRIDGE\_END & The time a bridge is ended \\ \hline
3WAY\_START & When a 3-way conf starts (usually via attended xfer) \\ \hline
3WAY\_END & When one or all exit a 3-way conf \\ \hline
BLINDTRANSFER & When a blind transfer is initiated \\ \hline
ATTENDEDTRANSFER & When an attended transfer is initiated \\ \hline
TRANSFER & Generic transfer initiated; not used yet...? \\ \hline
HOOKFLASH & So far, when a hookflash event occurs on a Zap interface \\ \hline
USER\_EVENT & these are triggered from the dialplan, and have a name given by the user. \\
\hline
\end{tabular}
\caption{List of CEL Events}
\label{event_table}
\end{table}
\begin{table}[h]
\begin{tabular}{ | l | p{10cm} | }
\hline
Field & Description \\ \hline \hline
eventtype & The name of the event; see the above list; each is prefixed with "EV\_". \\ \hline
eventtime & The time the event happened \\ \hline
cidname & CID name field \\ \hline
cidnum & CID number field \\ \hline
cidani & CID ANI field \\ \hline
cidrdnis & CID RDNIS field \\ \hline
ciddnid & CID DNID field \\ \hline
exten & The extension in the dialplan \\ \hline
context & The context in the dialplan \\ \hline
channame & The name assigned to the channel in which the event took place \\ \hline
appname & The name of the current application \\ \hline
appdata & The arguments that will be handed to that application \\ \hline
amaflags & The AMA flags associated with the event; user assignable. \\ \hline
accountcode & A user assigned datum (string) \\ \hline
uniqueid & Each Channel instance gets a unique ID associated with it. \\ \hline
userfield & A user assigned datum (string) \\ \hline
linkedid & the per-call id, spans several events, possibly. \\ \hline
peer & For bridge or other 2-channel events, this would be the other channel name \\
\hline
\end{tabular}
\caption{List of CEL Event Fields}
\label{field_table}
\end{table}
\section{Applications \& Functions}
\subsection{CEL Function}
**** THIS IS NO LONGER TRUE. REWRITE. ****
The CEL function parallels the CDR function, for fetching values from the
channel or event. It has some notable notable differences, though! For
instance, CEL data is not stored on the channel. Well, not much of it, anyway!
You can use the CEL function to set the amaflags, accountcode, and userfield,
which are stored on the channel.
Channel variables are not available for reading from the CEL function, nor can
any variable name other than what's in the list, be set. CDRs have a structure
attached to the channel, where the CDR function could access the values stored
there, or set the values there. CDRs could store their own variable lists, but
CEL has no such storage. There is no reason to store any event information, as
they are immediately output to the various backends at the time they are
generated.
See the description for the CEL function from the CLI: core show function CEL
Here is a list of all the available channel field names:
\begin{verbatim}
cidname userfield
cidnum amaflags
cidani cidrdnis
ciddnid appdata
exten accountcode
context uniqueid
channame appname
peer eventtime
eventtype
\end{verbatim}
\subsection{CELGenUserEvent Application}
This application allows the dialplan to insert custom events into the event
stream.
For more information, in the CLI, type: core show application CELGenUserEvent
Its arguments take this format:
\begin{verbatim}
CELGenUserEvent(eventname)
\end{verbatim}
Please note that there is no restrictions on the name supplied. If it happens to
match a standard CEL event name, it will look like that event was
generated. This could be a blessing or a curse!
\section{Configuration Files}
\begin{itemize}
\item cel.conf
\end{itemize}
\section{Generating Billing Information}
*** This is the Next Big Task ***

447
doc/tex/celdriver.tex Normal file
View File

@ -0,0 +1,447 @@
\section{Storage Backends}
Right now, the CEL package will support CSV, Customized CSV, ODBC, PGSQL, TDS,
Sqlite3, and Radius back ends. See the doc/celdriver.tex file
for how to use these back ends.
\subsection{Microsoft SQL Server}
Asterisk can currently store Channel Events into an MSSQL database in
two different ways: cel\_odbc or cel\_tds
Channel Event Records can be stored using unixODBC (which requires
the FreeTDS package) [cel\_odbc] or directly by using just the
FreeTDS package [cel\_tds] The following provide some
examples known to get asterisk working with mssql.
NOTE: Only choose one db connector.
\subsubsection{ODBC using cel\_odbc}
Compile, configure, and install the latest unixODBC package:
\begin{verbatim}
tar -zxvf unixODBC-2.2.9.tar.gz &&
cd unixODBC-2.2.9 &&
./configure --sysconfdir=/etc --prefix=/usr --disable-gui &&
make &&
make install
\end{verbatim}
Compile, configure, and install the latest FreeTDS package:
\begin{verbatim}
tar -zxvf freetds-0.62.4.tar.gz &&
cd freetds-0.62.4 &&
./configure --prefix=/usr --with-tdsver=7.0 \
--with-unixodbc=/usr/lib &&
make && make install
\end{verbatim}
Compile, or recompile, asterisk so that it will now add support
for cel\_odbc.
\begin{verbatim}
make clean && ./configure --with-odbc &&
make update &&
make &&
make install
\end{verbatim}
Setup odbc configuration files. These are working examples
from my system. You will need to modify for your setup.
You are not required to store usernames or passwords here.
\begin{verbatim}
/etc/odbcinst.ini
[FreeTDS]
Description = FreeTDS ODBC driver for MSSQL
Driver = /usr/lib/libtdsodbc.so
Setup = /usr/lib/libtdsS.so
FileUsage = 1
/etc/odbc.ini
[MSSQL-asterisk]
description = Asterisk ODBC for MSSQL
driver = FreeTDS
server = 192.168.1.25
port = 1433
database = voipdb
tds_version = 7.0
language = us_english
\end{verbatim}
Only install one database connector. Do not confuse asterisk
by using both ODBC (cel\_odbc) and FreeTDS (cel\_tds).
This command will erase the contents of cel\_tds.conf
\begin{verbatim}
[ -f /etc/asterisk/cel_tds.conf ] > /etc/asterisk/cel_tds.conf
\end{verbatim}
NOTE: unixODBC requires the freeTDS package, but asterisk does
not call freeTDS directly.
Now set up cel\_odbc configuration files. These are working samples
from my system. You will need to modify for your setup. Define
your usernames and passwords here, secure file as well.
\begin{verbatim}
/etc/asterisk/cel_odbc.conf
[global]
dsn=MSSQL-asterisk
username=voipdbuser
password=voipdbpass
loguniqueid=yes
\end{verbatim}
And finally, create the 'cel' table in your mssql database.
\begin{verbatim}
CREATE TABLE cel (
[eventtype] [varchar] (30) NOT NULL ,
[eventtime] [datetime] NOT NULL ,
[cidname] [varchar] (80) NOT NULL ,
[cidnum] [varchar] (80) NOT NULL ,
[cidani] [varchar] (80) NOT NULL ,
[cidrdnis] [varchar] (80) NOT NULL ,
[ciddnid] [varchar] (80) NOT NULL ,
[exten] [varchar] (80) NOT NULL ,
[context] [varchar] (80) NOT NULL ,
[channame] [varchar] (80) NOT NULL ,
[appname] [varchar] (80) NOT NULL ,
[appdata] [varchar] (80) NOT NULL ,
[amaflags] [int] NOT NULL ,
[accountcode] [varchar] (20) NOT NULL ,
[uniqueid] [varchar] (32) NOT NULL ,
[peer] [varchar] (80) NOT NULL ,
[userfield] [varchar] (255) NOT NULL
)
\end{verbatim}
Start asterisk in verbose mode, you should see that asterisk
logs a connection to the database and will now record every
desired channel event at the moment it occurs.
\subsubsection{FreeTDS, using cel\_tds}
Compile, configure, and install the latest FreeTDS package:
\begin{verbatim}
tar -zxvf freetds-0.62.4.tar.gz &&
cd freetds-0.62.4 &&
./configure --prefix=/usr --with-tdsver=7.0
make &&
make install
\end{verbatim}
Compile, or recompile, asterisk so that it will now add support
for cel\_tds.
\begin{verbatim}
make clean && ./configure --with-tds &&
make update &&
make &&
make install
\end{verbatim}
Only install one database connector. Do not confuse asterisk
by using both ODBC (cel\_odbc) and FreeTDS (cel\_tds).
This command will erase the contents of cel\_odbc.conf
\begin{verbatim}
[ -f /etc/asterisk/cel_odbc.conf ] > /etc/asterisk/cel_odbc.conf
\end{verbatim}
Setup cel\_tds configuration files. These are working samples
from my system. You will need to modify for your setup. Define
your usernames and passwords here, secure file as well.
\begin{verbatim}
/etc/asterisk/cel_tds.conf
[global]
hostname=192.168.1.25
port=1433
dbname=voipdb
user=voipdbuser
password=voipdpass
charset=BIG5
\end{verbatim}
And finally, create the 'cel' table in your mssql database.
\begin{verbatim}
CREATE TABLE cel (
[eventtype] [varchar] (30) NULL ,
[eventtime] [datetime] NULL ,
[cidname] [varchar] (80) NULL ,
[cidnum] [varchar] (80) NULL ,
[cidani] [varchar] (80) NULL ,
[cidrdnis] [varchar] (80) NULL ,
[ciddnid] [varchar] (80) NULL ,
[exten] [varchar] (80) NULL ,
[context] [varchar] (80) NULL ,
[channame] [varchar] (80) NULL ,
[appname] [varchar] (80) NULL ,
[appdata] [varchar] (80) NULL ,
[amaflags] [varchar] (16) NULL ,
[accountcode] [varchar] (20) NULL ,
[uniqueid] [varchar] (32) NULL ,
[userfield] [varchar] (255) NULL ,
[peer] [varchar] (80) NULL
)
\end{verbatim}
Start asterisk in verbose mode, you should see that asterisk
logs a connection to the database and will now record every
call to the database when it's complete.
\subsection{MySQL}
Using MySQL for Channel Event records is supported by using ODBC and the cel\_odbc module.
\subsection{PostreSQL}
If you want to go directly to postgresql database, and have the cel\_pgsql.so
compiled you can use the following sample setup.
On Debian, before compiling asterisk, just install libpqxx-dev.
Other distros will likely have a similiar package.
Once you have the compile done,
copy the sample cel\_pgsql.conf file or create your own.
Here is a sample:
\begin{verbatim}
/etc/asterisk/cel_pgsql.conf
; Sample Asterisk config file for CEL logging to PostgresSQL
[global]
hostname=localhost
port=5432
dbname=asterisk
password=password
user=postgres
table=cel
\end{verbatim}
Now create a table in postgresql for your cels
\begin{verbatim}
CREATE TABLE cel (
eventtype varchar (30) NOT NULL ,
eventtime time NOT NULL ,
cidname varchar (80) NOT NULL ,
cidnum varchar (80) NOT NULL ,
cidani varchar (80) NOT NULL ,
cidrdnis varchar (80) NOT NULL ,
ciddnis varchar (80) NOT NULL ,
exten varchar (80) NOT NULL ,
context varchar (80) NOT NULL ,
channame varchar (80) NOT NULL ,
appname varchar (80) NOT NULL ,
appdata varchar (80) NOT NULL ,
amaflags int NOT NULL ,
accountcode varchar (20) NOT NULL ,
uniqueid varchar (32) NOT NULL ,
userfield varchar (255) NOT NULL ,
peer varchar (80) NOT NULL
);
\end{verbatim}
\subsection{SQLite 3}
SQLite version 3 is supported in cel\_sqlite3\_custom.
\subsection{RADIUS}
\subsubsection{What is needed}
\begin{itemize}
\item FreeRADIUS server
\item Radiusclient-ng library
\item Asterisk PBX
\end{itemize}
\begin{figure}[h]
\begin{center}
\setlength{\unitlength}{4cm}
\begin{picture}(3,.75)
\put(0,0){\line(0,1){.75}}
\put(0,.75){\line(1,0){1.5}}
\put(1.5,0){\line(0,1){.75}}
\put(0,0){\line(1,0){1.5}}
\put(.1,.4){\makebox(1.3,.3){Asterisk PBX}}
\put(.1,.4){\line(1,0){1.3}}
\put(.1,.1){\line(1,0){1.3}}
\put(.1,.1){\line(0,1){.3}}
\put(1.4,.1){\line(0,1){.3}}
\put(.1,.1){\makebox(1.3,.3){RADIUS Client}}
\put(1.8,0){\line(0,1){.5}}
\put(1.8,.5){\line(1,0){1.1}}
\put(1.8,0){\line(1,0){1.1}}
\put(2.9,0){\line(0,1){.5}}
\put(1.8,.275){\makebox(1.1,.1){RADIUS Server}}
\put(1.8,.125){\makebox(1.1,.1){$(FreeRADIUS)$}}
\thicklines
\put(1.4,.3){\vector(1,0){.4}}
\put(1.8,.2){\vector(-1,0){.4}}
\thinlines
\end{picture}
\end{center}
\caption{Asterisk/RADIUS Integration}
\end{figure}
\subsubsection{Installation of the Radiusclient library}
Installation:
\begin{verbatim}
Download the sources from:
http://developer.berlios.de/projects/radiusclient-ng/
Untar the source tarball.
root@localhost:/usr/local/src# tar xvfz radiusclient-ng-0.5.5.1.tar.gz
Compile and install the library.
root@localhost:/usr/local/src# cd radiusclient-ng-0.5.5.1
root@localhost:/usr/local/src/radiusclient-ng-0.5.5.1# ./configure
root@localhost:/usr/local/src/radiusclient-ng-0.5.5.1# make
root@localhost:/usr/local/src/radiusclient-ng-0.5.5.1# make install
\end{verbatim}
\subsubsection{Configuration of the Radiusclient library}
By default all the configuration files of the radiusclient library will
be in /usr/local/etc/radiusclient-ng directory.
File "radiusclient.conf"
Open the file and find lines containing the following:
authserver localhost
This is the hostname or IP address of the RADIUS server used for
authentication. You will have to change this unless the server is
running on the same host as your Asterisk PBX.
acctserver localhost
This is the hostname or IP address of the RADIUS server used for
accounting. You will have to change this unless the server is running
on the same host as your Asterisk PBX.
File "servers"
RADIUS protocol uses simple access control mechanism based on shared
secrets that allows RADIUS servers to limit access from RADIUS clients.
A RADIUS server is configured with a secret string and only RADIUS
clients that have the same secret will be accepted.
You need to configure a shared secret for each server you have
configured in radiusclient.conf file in the previous step. The shared
secrets are stored in /usr/local/etc/radiusclient-ng/servers file.
Each line contains hostname of a RADIUS server and shared secret
used in communication with that server. The two values are separated
by white spaces. Configure shared secrets for every RADIUS server you
are going to use.
File "dictionary"
Asterisk uses some attributes that are not included in the
dictionary of radiusclient library, therefore it is necessary to add
them. A file called dictionary.digium (kept in the contrib dir)
was created to list all new attributes used by Asterisk.
Add to the end of the main dictionary file
/usr/local/etc/radiusclient-ng/dictionary
the line:
\begin{verbatim}
\$INCLUDE /path/to/dictionary.digium
\end{verbatim}
\subsubsection{Install FreeRADIUS Server (Version 1.1.1)}
Download sources tarball from:
http://freeradius.org/
Untar, configure, build, and install the server:
\begin{verbatim}
root@localhost:/usr/local/src# tar xvfz freeradius-1.1.1.tar.gz
root@localhost:/usr/local/src# cd freeradius-1.1.1
root@localhost"/usr/local/src/freeradius-1.1.1# ./configure
root@localhost"/usr/local/src/freeradius-1.1.1# make
root@localhost"/usr/local/src/freeradius-1.1.1# make install
\end{verbatim}
All the configuration files of FreeRADIUS server will be in
/usr/local/etc/raddb directory.
\subsubsection{Configuration of the FreeRADIUS Server}
There are several files that have to be modified to configure the
RADIUS server. These are presented next.
File "clients.conf"
File /usr/local/etc/raddb/clients.conf contains description of
RADIUS clients that are allowed to use the server. For each of the
clients you need to specify its hostname or IP address and also a
shared secret. The shared secret must be the same string you configured
in radiusclient library.
Example:
\begin{verbatim}
client myhost {
secret = mysecret
shortname = foo
}
\end{verbatim}
This fragment allows access from RADIUS clients on "myhost" if they use
"mysecret" as the shared secret.
The file already contains an entry for localhost (127.0.0.1), so if you
are running the RADIUS server on the same host as your Asterisk server,
then modify the existing entry instead, replacing the default password.
File "dictionary"
Note : as of version 1.1.2, the dictionary.digium file ships with FreeRADIUS.
The following procedure brings the dictionary.digium file to previous versions
of FreeRADIUS.
File /usr/local/etc/raddb/dictionary contains the dictionary of
FreeRADIUS server. You have to add the same dictionary file
(dictionary.digium), which you added to the dictionary of radiusclient-ng
library. You can include it into the main file, adding the following line at the
end of file '/usr/local/etc/raddb/dictionary':
\$INCLUDE /path/to/dictionary.digium
That will include the same new attribute definitions that are used
in radiusclient-ng library so the client and server will understand each
other.
\subsubsection{Asterisk Accounting Configuration}
Compilation and installation:
The module will be compiled as long as the radiusclient-ng
library has been detected on your system.
By default FreeRADIUS server will log all accounting requests into
/usr/local/var/log/radius/radacct directory in form of plain text files.
The server will create one file for each hostname in the directory. The
following example shows how the log files look like.
Asterisk now generates Call Detail Records. See /include/asterisk/cel.h
for all the fields which are recorded. By default, records in comma
separated values will be created in /var/log/asterisk/cel-csv.
The configuration file for cel\_radius.so module is :
/etc/asterisk/cel.conf
This is where you can set CEL related parameters as well as the path to
the radiusclient-ng library configuration file.
\subsubsection{Logged Values}
\begin{verbatim}
"Asterisk-Acc-Code", The account name of detail records
"Asterisk-CidName",
"Asterisk-CidNum",
"Asterisk-Cidani",
"Asterisk-Cidrdnis",
"Asterisk-Ciddnid",
"Asterisk-Exten",
"Asterisk-Context", The destination context
"Asterisk-Channame", The channel name
"Asterisk-Appname", Last application run on the channel
"Asterisk-App-Data", Argument to the last channel
"Asterisk-Event-Time",
"Asterisk-Event-Type",
"Asterisk-AMA-Flags", DOCUMENTATION, BILL, IGNORE etc, specified on
a per channel basis like accountcode.
"Asterisk-Unique-ID", Unique call identifier
"Asterisk-User-Field" User field set via SetCELUserField
"Asterisk-Peer" Name of the Peer for 2-channel events (like bridge)
\end{verbatim}

View File

@ -241,6 +241,8 @@ static int cdr_write(struct ast_channel *chan, const char *cmd, char *parse,
if (!strcasecmp(args.variable, "accountcode")) /* the 'l' flag doesn't apply to setting the accountcode, userfield, or amaflags */
ast_cdr_setaccount(chan, value);
else if (!strcasecmp(args.variable, "peeraccount"))
ast_cdr_setpeeraccount(chan, value);
else if (!strcasecmp(args.variable, "userfield"))
ast_cdr_setuserfield(chan, value);
else if (!strcasecmp(args.variable, "amaflags"))

View File

@ -29,6 +29,7 @@
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include <regex.h>
#include <ctype.h>
#include "asterisk/module.h"
#include "asterisk/channel.h"
@ -271,13 +272,62 @@ static int func_channel_read(struct ast_channel *chan, const char *function,
locked_copy_string(chan, buf, ast_state2str(chan->_state), len);
else if (!strcasecmp(data, "channeltype"))
locked_copy_string(chan, buf, chan->tech->type, len);
else if (!strcasecmp(data, "transfercapability"))
else if (!strcasecmp(data, "accountcode"))
locked_copy_string(chan, buf, chan->accountcode, len);
else if (!strcasecmp(data, "peeraccount"))
locked_copy_string(chan, buf, chan->peeraccount, len);
else if (!strcasecmp(data, "hangupsource"))
locked_copy_string(chan, buf, chan->hangupsource, len);
else if (!strcasecmp(data, "appname") && chan->appl)
locked_copy_string(chan, buf, chan->appl, len);
else if (!strcasecmp(data, "appdata") && chan->data)
locked_copy_string(chan, buf, chan->data, len);
else if (!strcasecmp(data, "exten") && chan->data)
locked_copy_string(chan, buf, chan->exten, len);
else if (!strcasecmp(data, "context") && chan->data)
locked_copy_string(chan, buf, chan->context, len);
else if (!strcasecmp(data, "userfield") && chan->data)
locked_copy_string(chan, buf, chan->userfield, len);
else if (!strcasecmp(data, "channame") && chan->data)
locked_copy_string(chan, buf, chan->name, len);
else if (!strcasecmp(data, "linkedid")) {
ast_channel_lock(chan);
if (ast_strlen_zero(chan->linkedid)) {
/* fall back on the channel's uniqueid if linkedid is unset */
ast_copy_string(buf, chan->uniqueid, len);
}
else {
ast_copy_string(buf, chan->linkedid, len);
}
ast_channel_unlock(chan);
} else if (!strcasecmp(data, "peer")) {
struct ast_channel *p;
ast_channel_lock(chan);
p = ast_bridged_channel(chan);
if (p || chan->tech || chan->cdr) /* dummy channel? if so, we hid the peer name in the language */
ast_copy_string(buf, (p ? p->name : ""), len);
else {
/* a dummy channel can still pass along bridged peer info via
the BRIDGEPEER variable */
const char *pname = pbx_builtin_getvar_helper(chan, "BRIDGEPEER");
if (!ast_strlen_zero(pname))
ast_copy_string(buf, pname, len); /* a horrible kludge, but... how else? */
else
buf[0] = 0;
}
ast_channel_unlock(chan);
} else if (!strcasecmp(data, "uniqueid")) {
locked_copy_string(chan, buf, chan->uniqueid, len);
} else if (!strcasecmp(data, "transfercapability"))
locked_copy_string(chan, buf, transfercapability_table[chan->transfercapability & 0x1f], len);
else if (!strcasecmp(data, "callgroup")) {
char groupbuf[256];
locked_copy_string(chan, buf, ast_print_group(groupbuf, sizeof(groupbuf), chan->callgroup), len);
} else if (!chan->tech->func_channel_read
|| chan->tech->func_channel_read(chan, function, data, buf, len)) {
} else if (!strcasecmp(data, "amaflags")) {
char amabuf[256];
snprintf(amabuf,sizeof(amabuf), "%d", chan->amaflags);
locked_copy_string(chan, buf, amabuf, len);
} else if (!chan->tech || !chan->tech->func_channel_read || chan->tech->func_channel_read(chan, function, data, buf, len)) {
ast_log(LOG_WARNING, "Unknown or unavailable item requested: '%s'\n", data);
ret = -1;
}
@ -297,12 +347,33 @@ static int func_channel_write(struct ast_channel *chan, const char *function,
locked_string_field_set(chan, parkinglot, value);
else if (!strcasecmp(data, "musicclass"))
locked_string_field_set(chan, musicclass, value);
else if (!strcasecmp(data, "accountcode"))
locked_string_field_set(chan, accountcode, value);
else if (!strcasecmp(data, "userfield"))
locked_string_field_set(chan, userfield, value);
else if (!strcasecmp(data, "amaflags")) {
ast_channel_lock(chan);
if(isdigit(*value)) {
sscanf(value, "%d", &chan->amaflags);
} else if (!strcasecmp(value,"OMIT")){
chan->amaflags = 1;
} else if (!strcasecmp(value,"BILLING")){
chan->amaflags = 2;
} else if (!strcasecmp(value,"DOCUMENTATION")){
chan->amaflags = 3;
}
ast_channel_unlock(chan);
} else if (!strcasecmp(data, "peeraccount"))
locked_string_field_set(chan, peeraccount, value);
else if (!strcasecmp(data, "hangupsource"))
/* XXX - should we be forcing this here? */
ast_set_hangupsource(chan, value, 0);
#ifdef CHANNEL_TRACE
else if (!strcasecmp(data, "trace")) {
ast_channel_lock(chan);
if (ast_true(value))
ret = ast_channel_trace_enable(chan);
else if (ast_false(value))
else if (ast_false(value))
ret = ast_channel_trace_disable(chan);
else {
ret = -1;

View File

@ -238,7 +238,7 @@ static int acf_odbc_write(struct ast_channel *chan, const char *cmd, char *s, co
}
if (!chan) {
if ((chan = ast_channel_alloc(0, 0, "", "", "", "", "", 0, "Bogus/func_odbc")))
if ((chan = ast_channel_alloc(0, AST_STATE_DOWN, "", "", "", "", "", "", 0, "Bogus/func_odbc")))
bogus_chan = 1;
}
@ -414,7 +414,7 @@ static int acf_odbc_read(struct ast_channel *chan, const char *cmd, char *s, cha
}
if (!chan) {
if ((chan = ast_channel_alloc(0, 0, "", "", "", "", "", 0, "Bogus/func_odbc"))) {
if ((chan = ast_channel_alloc(0, AST_STATE_DOWN, "", "", "", "", "", "", 0, "Bogus/func_odbc"))) {
bogus_chan = 1;
}
}
@ -1051,7 +1051,7 @@ static char *cli_odbc_read(struct ast_cli_entry *e, int cmd, struct ast_cli_args
/* Evaluate function */
char_args = ast_strdupa(a->argv[3]);
chan = ast_channel_alloc(0, 0, "", "", "", "", "", 0, "Bogus/func_odbc");
chan = ast_dummy_channel_alloc();
AST_STANDARD_APP_ARGS(args, char_args);
for (i = 0; i < args.argc; i++) {
@ -1254,7 +1254,7 @@ static char *cli_odbc_write(struct ast_cli_entry *e, int cmd, struct ast_cli_arg
char_args = ast_strdupa(a->argv[3]);
char_values = ast_strdupa(a->argv[4]);
chan = ast_channel_alloc(0, 0, "", "", "", "", "", 0, "Bogus/func_odbc");
chan = ast_dummy_channel_alloc();
AST_STANDARD_APP_ARGS(args, char_args);
for (i = 0; i < args.argc; i++) {

View File

@ -42,6 +42,8 @@ int ast_timing_init(void); /*!< Provided by timing.c */
int ast_indications_init(void); /*!< Provided by indications.c */
int ast_indications_reload(void);/*!< Provided by indications.c */
void ast_stun_init(void); /*!< Provided by stun.c */
int ast_cel_engine_init(void); /*!< Provided by cel.c */
int ast_cel_engine_reload(void); /*!< Provided by cel.c */
/*!
* \brief Reload asterisk modules.

View File

@ -94,16 +94,20 @@ struct ast_cdr {
/*! Total time call is up, in seconds */
long int billsec;
/*! What happened to the call */
long int disposition;
long int disposition;
/*! What flags to use */
long int amaflags;
long int amaflags;
/*! What account number to use */
char accountcode[AST_MAX_ACCOUNT_CODE];
char accountcode[AST_MAX_ACCOUNT_CODE];
/*! Account number of the last person we talked to */
char peeraccount[AST_MAX_ACCOUNT_CODE];
/*! flags */
unsigned int flags;
unsigned int flags;
/*! Unique Channel Identifier
* 150 = 127 (max systemname) + "-" + 10 (epoch timestamp) + "." + 10 (monotonically incrementing integer) + NULL */
char uniqueid[150];
/* Linked group Identifier */
char linkedid[32];
/*! User field */
char userfield[AST_MAX_USER_FIELD];
@ -353,6 +357,9 @@ void ast_cdr_merge(struct ast_cdr *to, struct ast_cdr *from);
/*! \brief Set account code, will generate AMI event */
int ast_cdr_setaccount(struct ast_channel *chan, const char *account);
/*! \brief Set the peer account */
int ast_cdr_setpeeraccount(struct ast_channel *chan, const char *account);
/*! \brief Set AMA flags for channel */
int ast_cdr_setamaflags(struct ast_channel *chan, const char *amaflags);

276
include/asterisk/cel.h Normal file
View File

@ -0,0 +1,276 @@
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 2008 - 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 Call Event Logging API
*/
#ifndef __AST_CEL_H__
#define __AST_CEL_H__
#if defined(__cplusplus) || defined(c_plusplus)
extern "C" {
#endif
#include "asterisk/event.h"
/*! \brief AMA Flags */
enum ast_cel_ama_flag {
AST_CEL_AMA_FLAG_OMIT,
AST_CEL_AMA_FLAG_BILLING,
AST_CEL_AMA_FLAG_DOCUMENTATION,
/*! \brief Must be final entry */
AST_CEL_AMA_FLAG_TOTAL,
};
/*!
* \brief CEL event types
*/
enum ast_cel_event_type {
/*! \brief channel birth */
AST_CEL_CHANNEL_START = 1,
/*! \brief channel end */
AST_CEL_CHANNEL_END = 2,
/*! \brief hangup terminates connection */
AST_CEL_HANGUP = 3,
/*! \brief A ringing phone is answered */
AST_CEL_ANSWER = 4,
/*! \brief an app starts */
AST_CEL_APP_START = 5,
/*! \brief an app ends */
AST_CEL_APP_END = 6,
/*! \brief a bridge is established */
AST_CEL_BRIDGE_START = 7,
/*! \brief a bridge is torn down */
AST_CEL_BRIDGE_END = 8,
/*! \brief a conference is started */
AST_CEL_CONF_START = 9,
/*! \brief a conference is ended */
AST_CEL_CONF_END = 10,
/*! \brief a channel is parked */
AST_CEL_PARK_START = 11,
/*! \brief channel out of the park */
AST_CEL_PARK_END = 12,
/*! \brief a transfer occurs */
AST_CEL_BLINDTRANSFER = 13,
/*! \brief a transfer occurs */
AST_CEL_ATTENDEDTRANSFER = 14,
/*! \brief a transfer occurs */
AST_CEL_TRANSFER = 15,
/*! \brief a 3-way conference, usually part of a transfer */
AST_CEL_HOOKFLASH = 16,
/*! \brief a 3-way conference, usually part of a transfer */
AST_CEL_3WAY_START = 17,
/*! \brief a 3-way conference, usually part of a transfer */
AST_CEL_3WAY_END = 18,
/*! \brief channel enters a conference */
AST_CEL_CONF_ENTER = 19,
/*! \brief channel exits a conference */
AST_CEL_CONF_EXIT = 20,
/*! \brief a user-defined event, the event name field should be set */
AST_CEL_USER_DEFINED = 21,
/*! \brief the last channel with the given linkedid is retired */
AST_CEL_LINKEDID_END = 22,
/*! \brief a masquerade happened to alter the participants on a bridge */
AST_CEL_BRIDGE_UPDATE = 23,
/*! \brief a directed pickup was performed on this channel */
AST_CEL_PICKUP = 24,
/*! \brief this call was forwarded somewhere else */
AST_CEL_FORWARD = 25,
};
/*!
* \brief Check to see if CEL is enabled
*
* \since 1.6.3
*
* \retval zero not enabled
* \retval non-zero enabled
*/
unsigned int ast_cel_check_enabled(void);
/*!
* \brief Allocate a CEL record
*
* \since 1.6.3
*
* \note The CEL record must be destroyed with ast_cel_destroy().
*
* \retval non-NULL an allocated ast_cel structure
* \retval NULL error
*/
struct ast_cel *ast_cel_alloc(void);
/*!
* \brief Destroy a CEL record.
*
* \param cel the record to destroy
*
* \since 1.6.3
*
* \return nothing.
*/
void ast_cel_destroy(struct ast_cel *cel);
/*!
* \brief Get the name of a CEL event type
*
* \param type the type to get the name of
*
* \since 1.6.3
*
* \return the string representation of the type
*/
const char *ast_cel_get_type_name(enum ast_cel_event_type type);
/*!
* \brief Get the event type from a string
*
* \param name the event type name as a string
*
* \since 1.6.3
*
* \return the ast_cel_event_type given by the string
*/
enum ast_cel_event_type ast_cel_str_to_event_type(const char *name);
/*!
* \brief Convert AMA flag to printable string
*
* \param[in] flag the flag to convert to a string
*
* \since 1.6.3
*
* \return the string representation of the flag
*/
const char *ast_cel_get_ama_flag_name(enum ast_cel_ama_flag flag);
/*!
* \brief Check and potentially retire a Linked ID
*
* \param chan channel that is being destroyed or its linkedid is changing
*
* \since 1.6.3
*
* If at least one CEL backend is looking for CEL_LINKEDID_END
* events, this function will check if the given channel is the last
* active channel with that linkedid, and if it is, emit a
* CEL_LINKEDID_END event.
*
* \return nothing
*/
void ast_cel_check_retire_linkedid(struct ast_channel *chan);
/*!
* \brief Create a fake channel from data in a CEL event
*
* This function creates a fake channel containing the serialized channel data
* in the given cel event. It must be released with ast_channel_release.
*
* \param event the CEL event
*
* \since 1.6.3
*
* \return a channel with the data filled in, or NULL on error
*
* \todo This function is \b very expensive, especially given that some CEL backends
* use it on \b every CEL event. This function really needs to go away at
* some point.
*/
struct ast_channel *ast_cel_fabricate_channel_from_event(const struct ast_event *event);
/*!
* \brief Report a channel event
*
* \param chan This argument is required. This is the primary channel associated with
* this channel event.
* \param event_type This is the type of call event being reported.
* \param userdefevname This is an optional custom name for the call event.
* \param extra This is an optional opaque field that will go into the "CEL_EXTRA"
* information element of the call event.
* \param peer2 All CEL events contain a "peer name" information element. The first
* place the code will look to get a peer name is from the bridged channel to
* chan. If chan has no bridged channel and peer2 is specified, then the name
* of peer2 will go into the "peer name" field. If neither are available, the
* peer name field will be blank.
*
* \since 1.6.3
*
* \pre chan and peer2 are both unlocked
*
* \retval 0 success
* \retval non-zero failure
*/
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);
/*!
* \brief Helper struct for getting the fields out of a CEL event
*/
struct ast_cel_event_record {
/*!
* \brief struct ABI version
* \note This \b must be incremented when the struct changes.
*/
#define AST_CEL_EVENT_RECORD_VERSION 2
/*!
* \brief struct ABI version
* \note This \b must stay as the first member.
*/
uint32_t version;
enum ast_cel_event_type event_type;
struct timeval event_time;
const char *event_name;
const char *user_defined_name;
const char *caller_id_name;
const char *caller_id_num;
const char *caller_id_ani;
const char *caller_id_rdnis;
const char *caller_id_dnid;
const char *extension;
const char *context;
const char *channel_name;
const char *application_name;
const char *application_data;
const char *account_code;
const char *peer_account;
const char *unique_id;
const char *linked_id;
uint amaflag;
const char *user_field;
const char *peer;
const char *extra;
};
/*!
* \brief Fill in an ast_cel_event_record from a CEL event
*
* \param[in] event the CEL event
* \param[out] r the ast_cel_event_record to fill in
*
* \since 1.6.3
*
* \retval 0 success
* \retval non-zero failure
*/
int ast_cel_fill_record(const struct ast_event *event, struct ast_cel_event_record *r);
#if defined(__cplusplus) || defined(c_plusplus)
}
#endif
#endif /* __AST_CEL_H__ */

View File

@ -361,7 +361,7 @@ struct ast_channel_tech {
int properties; /*!< Technology Properties */
/*! \brief Requester - to set up call data structures (pvt's) */
struct ast_channel *(* const requester)(const char *type, int format, void *data, int *cause);
struct ast_channel *(* const requester)(const char *type, int format, const struct ast_channel *requestor, void *data, int *cause);
int (* const devicestate)(void *data); /*!< Devicestate call back */
@ -612,9 +612,13 @@ struct ast_channel {
AST_STRING_FIELD(language); /*!< Language requested for voice prompts */
AST_STRING_FIELD(musicclass); /*!< Default music class */
AST_STRING_FIELD(accountcode); /*!< Account code for billing */
AST_STRING_FIELD(peeraccount); /*!< Peer account code for billing */
AST_STRING_FIELD(userfield); /*!< Userfield for CEL billing */
AST_STRING_FIELD(call_forward); /*!< Where to forward to if asked to dial on this interface */
AST_STRING_FIELD(uniqueid); /*!< Unique Channel Identifier */
AST_STRING_FIELD(linkedid); /*!< Linked Channel Identifier -- gets propagated by linkage */
AST_STRING_FIELD(parkinglot); /*! Default parking lot, if empty, default parking lot */
AST_STRING_FIELD(hangupsource); /*! Who is responsible for hanging up this channel */
AST_STRING_FIELD(dialcontext); /*!< Dial: Extension context that we were called from */
);
@ -913,17 +917,30 @@ int ast_setstate(struct ast_channel *chan, enum ast_channel_state);
* \note By default, new channels are set to the "s" extension
* and "default" context.
*/
struct ast_channel * attribute_malloc __attribute__((format(printf, 12, 13)))
struct ast_channel * attribute_malloc __attribute__((format(printf, 13, 14)))
__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, ...);
#define ast_channel_alloc(needqueue, state, cid_num, cid_name, acctcode, exten, context, amaflag, ...) \
__ast_channel_alloc(needqueue, state, cid_num, cid_name, acctcode, exten, context, amaflag, \
#define ast_channel_alloc(needqueue, state, cid_num, cid_name, acctcode, exten, context, linkedid, amaflag, ...) \
__ast_channel_alloc(needqueue, state, cid_num, cid_name, acctcode, exten, context, linkedid, amaflag, \
__FILE__, __LINE__, __FUNCTION__, __VA_ARGS__)
/*!
* \brief Create a fake channel structure
*
* \retval NULL failure
* \retval non-NULL successfully allocated channel
*
* \note This function should ONLY be used to create a fake channel
* that can then be populated with data for use in variable
* substitution when a real channel does not exist.
*/
struct ast_channel *ast_dummy_channel_alloc(void);
/*!
* \brief Queue one or more frames to a channel's frame queue
*
@ -1035,7 +1052,7 @@ void ast_change_name(struct ast_channel *chan, const char *newname);
*/
struct ast_channel *ast_channel_release(struct ast_channel *chan);
/*!
/*!
* \brief Requests a channel
*
* \param type type of channel to request
@ -1050,7 +1067,7 @@ struct ast_channel *ast_channel_release(struct ast_channel *chan);
* \retval NULL failure
* \retval non-NULL channel on success
*/
struct ast_channel *ast_request(const char *type, int format, void *data, int *status);
struct ast_channel *ast_request(const char *type, int format, const struct ast_channel *requestor, void *data, int *status);
/*!
* \brief Request a channel of a given type, with data as optional information used
@ -1067,7 +1084,7 @@ struct ast_channel *ast_request(const char *type, int format, void *data, int *s
* \return Returns an ast_channel on success or no answer, NULL on failure. Check the value of chan->_state
* to know if the call was answered or not.
*/
struct ast_channel *ast_request_and_dial(const char *type, int format, void *data,
struct ast_channel *ast_request_and_dial(const char *type, int format, const struct ast_channel *requestor, void *data,
int timeout, int *reason, const char *cid_num, const char *cid_name);
/*!
@ -1084,7 +1101,7 @@ struct ast_channel *ast_request_and_dial(const char *type, int format, void *dat
* \return Returns an ast_channel on success or no answer, NULL on failure. Check the value of chan->_state
* to know if the call was answered or not.
*/
struct ast_channel *__ast_request_and_dial(const char *type, int format, void *data,
struct ast_channel *__ast_request_and_dial(const char *type, int format, const struct ast_channel *requestor, void *data,
int timeout, int *reason, const char *cid_num, const char *cid_name, struct outgoing_helper *oh);
/*!
@ -1188,6 +1205,16 @@ int ast_softhangup(struct ast_channel *chan, int cause);
*/
int ast_softhangup_nolock(struct ast_channel *chan, int cause);
/*! \brief Set the source of the hangup in this channel and it's bridge
* \param chan channel to set the field on
* \param source a string describing the source of the hangup for this channel
*
* Hangupsource is generally the channel name that caused the bridge to be
* hung up, but it can also be other things such as "dialplan/agi"
* This can then be logged in the CDR or CEL
*/
void ast_set_hangupsource(struct ast_channel *chan, const char *source, int force);
/*! \brief Check to see if a channel is needing hang up
* \param chan channel on which to check for hang up
* This function determines if the channel is being requested to be hung up.
@ -2280,6 +2307,12 @@ struct ast_channel *ast_channel_get_by_exten(const char *exten, const char *cont
/*! @} End channel search functions. */
/*!
\brief propagate the linked id between chan and peer
*/
void ast_channel_set_linkgroup(struct ast_channel *chan, struct ast_channel *peer);
/*!
* \since 1.6.3
* \brief Copy the source caller information to the destination caller.

View File

@ -111,7 +111,7 @@ typedef void (*ast_event_cb_t)(const struct ast_event *event, void *userdata);
* pointer to the peer.
*/
struct ast_event_sub *ast_event_subscribe(enum ast_event_type event_type,
ast_event_cb_t cb, void *userdata, ...);
ast_event_cb_t cb, char *description, void *userdata, ...);
/*!
* \brief Allocate a subscription, but do not activate it
@ -241,6 +241,15 @@ int ast_event_sub_activate(struct ast_event_sub *sub);
*/
struct ast_event_sub *ast_event_unsubscribe(struct ast_event_sub *event_sub);
/*!
* \brief Get description for a subscription
*
* \param sub subscription
*
* \return string description of the subscription
*/
const char *ast_event_subscriber_get_description(struct ast_event_sub *sub);
/*!
* \brief Check if subscribers exist
*

View File

@ -48,8 +48,10 @@ enum ast_event_type {
/*! The state of a device has changed on _one_ server. This should not be used
* directly, in general. Use AST_EVENT_DEVICE_STATE instead. */
AST_EVENT_DEVICE_STATE_CHANGE = 0x06,
/*! Channel Event Logging events */
AST_EVENT_CEL = 0x07,
/*! Number of event types. This should be the last event type + 1 */
AST_EVENT_TOTAL = 0x07,
AST_EVENT_TOTAL = 0x08,
};
/*! \brief Event Information Element types */
@ -92,7 +94,7 @@ enum ast_event_ie_type {
* Used by: AST_EVENT_SUB
* Payload type: UINT (ast_event_ie_type)
*/
AST_EVENT_IE_EXISTS = 0x06,
AST_EVENT_IE_EXISTS = 0x6,
/*!
* \brief Device Name
* Used by AST_EVENT_DEVICE_STATE_CHANGE
@ -113,13 +115,151 @@ enum ast_event_ie_type {
* Payload type: str
*/
AST_EVENT_IE_CONTEXT = 0x09,
/*!
* \brief Entity ID
* Used by All events
* Payload type: RAW
* This IE indicates which server the event originated from
*/
AST_EVENT_IE_EID = 0x0A,
/*!
* \brief Channel Event Type
* Used by: AST_EVENT_CEL
* Payload type: UINT
*/
AST_EVENT_IE_CEL_EVENT_TYPE = 0x0a,
/*!
* \brief Channel Event Time (seconds)
* Used by: AST_EVENT_CEL
* Payload type: UINT
*/
AST_EVENT_IE_CEL_EVENT_TIME = 0x0b,
/*!
* \brief Channel Event Time (micro-seconds)
* Used by: AST_EVENT_CEL
* Payload type: UINT
*/
AST_EVENT_IE_CEL_EVENT_TIME_USEC = 0x0c,
/*!
* \brief Channel Event User Event Name
* Used by: AST_EVENT_CEL
* Payload type: STR
*/
AST_EVENT_IE_CEL_USEREVENT_NAME = 0x0d,
/*!
* \brief Channel Event CID name
* Used by: AST_EVENT_CEL
* Payload type: STR
*/
AST_EVENT_IE_CEL_CIDNAME = 0x0e,
/*!
* \brief Channel Event CID num
* Used by: AST_EVENT_CEL
* Payload type: STR
*/
AST_EVENT_IE_CEL_CIDNUM = 0x0f,
/*!
* \brief Channel Event extension name
* Used by: AST_EVENT_CEL
* Payload type: STR
*/
AST_EVENT_IE_CEL_EXTEN = 0x10,
/*!
* \brief Channel Event context name
* Used by: AST_EVENT_CEL
* Payload type: STR
*/
AST_EVENT_IE_CEL_CONTEXT = 0x11,
/*!
* \brief Channel Event channel name
* Used by: AST_EVENT_CEL
* Payload type: STR
*/
AST_EVENT_IE_CEL_CHANNAME = 0x12,
/*!
* \brief Channel Event app name
* Used by: AST_EVENT_CEL
* Payload type: STR
*/
AST_EVENT_IE_CEL_APPNAME = 0x13,
/*!
* \brief Channel Event app args/data
* Used by: AST_EVENT_CEL
* Payload type: STR
*/
AST_EVENT_IE_CEL_APPDATA = 0x14,
/*!
* \brief Channel Event AMA flags
* Used by: AST_EVENT_CEL
* Payload type: UINT
*/
AST_EVENT_IE_CEL_AMAFLAGS = 0x15,
/*!
* \brief Channel Event AccountCode
* Used by: AST_EVENT_CEL
* Payload type: STR
*/
AST_EVENT_IE_CEL_ACCTCODE = 0x16,
/*!
* \brief Channel Event UniqueID
* Used by: AST_EVENT_CEL
* Payload type: STR
*/
AST_EVENT_IE_CEL_UNIQUEID = 0x17,
/*!
* \brief Channel Event Userfield
* Used by: AST_EVENT_CEL
* Payload type: STR
*/
AST_EVENT_IE_CEL_USERFIELD = 0x18,
/*!
* \brief Channel Event CID ANI field
* Used by: AST_EVENT_CEL
* Payload type: STR
*/
AST_EVENT_IE_CEL_CIDANI = 0x19,
/*!
* \brief Channel Event CID RDNIS field
* Used by: AST_EVENT_CEL
* Payload type: STR
*/
AST_EVENT_IE_CEL_CIDRDNIS = 0x1a,
/*!
* \brief Channel Event CID dnid
* Used by: AST_EVENT_CEL
* Payload type: STR
*/
AST_EVENT_IE_CEL_CIDDNID = 0x1b,
/*!
* \brief Channel Event Peer -- for Things involving multiple channels, like BRIDGE
* Used by: AST_EVENT_CEL
* Payload type: STR
*/
AST_EVENT_IE_CEL_PEER = 0x1c,
/*!
* \brief Channel Event LinkedID
* Used by: AST_EVENT_CEL
* Payload type: STR
*/
AST_EVENT_IE_CEL_LINKEDID = 0x1d,
/*!
* \brief Channel Event peeraccount
* Used by: AST_EVENT_CEL
* Payload type: STR
*/
AST_EVENT_IE_CEL_PEERACCT = 0x1e,
/*!
* \brief Channel Event extra data
* Used by: AST_EVENT_CEL
* Payload type: STR
*/
AST_EVENT_IE_CEL_EXTRA = 0x1f,
/*!
* \brief Description
* Used by: AST_EVENT_SUB, AST_EVENT_UNSUB
* Payload type: STR
*/
AST_EVENT_IE_DESCRIPTION = 0x20,
/*!
* \brief Entity ID
* Used by All events
* Payload type: RAW
* This IE indicates which server the event originated from
*/
AST_EVENT_IE_EID = 0x21,
};
#define AST_EVENT_IE_MAX AST_EVENT_IE_EID

View File

@ -27,6 +27,7 @@
#include <time.h> /* we want to override localtime_r */
#include <unistd.h>
#include <string.h>
#include "asterisk/lock.h"
#include "asterisk/time.h"

View File

@ -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);

View File

@ -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));

652
main/cel.c Normal file
View File

@ -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();
}

View File

@ -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);

View File

@ -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)"),

View File

@ -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");

View File

@ -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";

View File

@ -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;
}

View File

@ -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"

View File

@ -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 }
};

View File

@ -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));

View File

@ -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);

View File

@ -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;
}

View File

@ -325,7 +325,7 @@ static void add_publish_event(struct event_channel *event_channel, const char *e
publish_event->type = type;
ast_log(LOG_DEBUG, "Subscribing to event type %d\n", type);
publish_event->sub = ast_event_subscribe(type, ast_event_cb, event_channel,
publish_event->sub = ast_event_subscribe(type, ast_event_cb, "AIS", event_channel,
AST_EVENT_IE_END);
ast_event_dump_cache(publish_event->sub);

View File

@ -2256,6 +2256,7 @@ static int handle_hangup(struct ast_channel *chan, AGI *agi, int argc, const cha
if (argc == 1) {
/* no argument: hangup the current channel */
ast_set_hangupsource(chan, "dialplan/agi", 0);
ast_softhangup(chan,AST_SOFTHANGUP_EXPLICIT);
ast_agi_send(agi->fd, chan, "200 result=1\n");
return RESULT_SUCCESS;
@ -2263,6 +2264,7 @@ static int handle_hangup(struct ast_channel *chan, AGI *agi, int argc, const cha
/* one argument: look for info on the specified channel */
if ((c = ast_channel_get_by_name(argv[1]))) {
/* we have a matching channel */
ast_set_hangupsource(c, "dialplan/agi", 0);
ast_softhangup(c, AST_SOFTHANGUP_EXPLICIT);
c = ast_channel_unref(c);
ast_agi_send(agi->fd, chan, "200 result=1\n");

View File

@ -631,7 +631,7 @@ static int calendar_event_notify(const void *data)
ast_dial_set_global_timeout(dial, event->owner->notify_waittime);
generate_random_string(buf, sizeof(buf));
if (!(chan = ast_channel_alloc(1, AST_STATE_DOWN, 0, 0, 0, 0, 0, 0, "Calendar/%s-%s", event->owner->name, buf))) {
if (!(chan = ast_channel_alloc(1, AST_STATE_DOWN, 0, 0, 0, 0, 0, 0, 0, "Calendar/%s-%s", event->owner->name, buf))) {
ast_log(LOG_ERROR, "Could not allocate notification channel\n");
goto notify_cleanup;
}

View File

@ -165,7 +165,7 @@ static char *handle_cli_test_substitution(struct ast_cli_entry *e, int cmd, stru
}
ast_cli(a->fd, "Testing variable substitution ...\n");
c = ast_channel_alloc(0, 0, "", "", "", "", "", 0, "Test/substitution");
c = ast_dummy_channel_alloc();
test_chan_integer(a->fd, c, &c->cid.cid_pres, "${CALLINGPRES}");
test_chan_integer(a->fd, c, &c->cid.cid_ani2, "${CALLINGANI2}");
@ -217,7 +217,7 @@ static char *handle_cli_test_substitution(struct ast_cli_entry *e, int cmd, stru
ast_free(cmd);
}
ast_hangup(c);
ast_channel_release(c);
return CLI_SUCCESS;
}