Sat Apr 26 2014 22:02:01

Asterisk developer's documentation


cel.c File Reference

Channel Event Logging API. More...

#include "asterisk.h"
#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"
Include dependency graph for cel.c:

Go to the source code of this file.

Data Structures

struct  cel_linkedid

Defines

#define CEL_DEFAULT_EVENTS   0
 Track no events by default.
#define CEL_ENABLED_DEFAULT   0
 CEL is off by default.
#define CEL_MAX_EVENT_IDS   64
 Maximum possible CEL event IDs.
#define NUM_APP_BUCKETS   97
 Number of buckets for the appset container.

Functions

static int app_cmp (void *obj, void *arg, int flags)
static int app_hash (const void *obj, const int flags)
unsigned int ast_cel_check_enabled (void)
 Check to see if CEL is enabled.
void ast_cel_check_retire_linkedid (struct ast_channel *chan)
 Check and potentially retire a Linked ID.
int ast_cel_engine_init (void)
int ast_cel_engine_reload (void)
static void ast_cel_engine_term (void)
struct ast_channelast_cel_fabricate_channel_from_event (const struct ast_event *event)
 Create a fake channel from data in a CEL event.
int ast_cel_fill_record (const struct ast_event *e, struct ast_cel_event_record *r)
 Fill in an ast_cel_event_record from a CEL event.
const char * ast_cel_get_ama_flag_name (enum ast_cel_ama_flag flag)
 Convert AMA flag to printable string.
const char * ast_cel_get_type_name (enum ast_cel_event_type type)
 Get the name of a CEL event type.
int ast_cel_linkedid_ref (const char *linkedid)
 Inform CEL that a new linkedid is being used.
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)
 Report a channel event.
enum ast_cel_event_type ast_cel_str_to_event_type (const char *name)
 Get the event type from a string.
static int ast_cel_track_event (enum ast_cel_event_type et)
static int do_reload (int is_reload)
static char * handle_cli_status (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static int lid_cmp (void *obj, void *arg, int flags)
static int lid_hash (const void *obj, const int flags)
static void parse_apps (const char *val)
static void parse_events (const char *val)
static void print_cel_sub (const struct ast_event *event, void *data)
static void set_defaults (void)

Variables

static struct ao2_containerappset
 Container of Asterisk application names.
static const char *const cel_ama_flags [AST_CEL_AMA_FLAG_TOTAL]
 Map of ast_cel_ama_flags to strings.
static const char cel_conf_file [] = "cel.conf"
static char cel_dateformat [256]
 Configured date format for event timestamps.
static unsigned char cel_enabled
static const char *const cel_event_types [CEL_MAX_EVENT_IDS]
 Map of ast_cel_event_type to strings.
static struct ast_cli_entry cli_status = AST_CLI_DEFINE(handle_cli_status, "Display the CEL status")
static int64_t eventset
 which events we want to track
static struct ast_datastore_info fabricated_channel_datastore
static struct ao2_containerlinkedids
 Container of channel references to a linkedid for CEL purposes.
static ast_mutex_t reload_lock = { PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP , NULL, 1 }
 Lock protecting CEL.

Detailed Description

Channel Event Logging API.

Author:
Steve Murphy <murf@digium.com>
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.

Definition in file cel.c.


Define Documentation

#define CEL_DEFAULT_EVENTS   0

Track no events by default.

Definition at line 75 of file cel.c.

Referenced by set_defaults().

#define CEL_ENABLED_DEFAULT   0

CEL is off by default.

Definition at line 57 of file cel.c.

Referenced by set_defaults().

#define CEL_MAX_EVENT_IDS   64

Maximum possible CEL event IDs.

Note:
This limit is currently imposed by the eventset definition

Definition at line 70 of file cel.c.

#define NUM_APP_BUCKETS   97

Number of buckets for the appset container.

Definition at line 80 of file cel.c.

Referenced by ast_cel_engine_init().


Function Documentation

static int app_cmp ( void *  obj,
void *  arg,
int  flags 
) [static]

Definition at line 813 of file cel.c.

References app1, app2, and CMP_MATCH.

Referenced by ast_cel_engine_init().

{
   const char *app1 = obj;
   const char *app2 = arg;

   return !strcasecmp(app1, app2) ? CMP_MATCH : 0;
}
static int app_hash ( const void *  obj,
const int  flags 
) [static]

Definition at line 808 of file cel.c.

References ast_str_case_hash().

Referenced by ast_cel_engine_init().

{
   return ast_str_case_hash((const char *) obj);
}
unsigned int ast_cel_check_enabled ( void  )

Check to see if CEL is enabled.

Since:
1.8
Return values:
zeronot enabled
non-zeroenabled

Definition at line 164 of file cel.c.

References cel_enabled.

{
   return cel_enabled;
}
void ast_cel_check_retire_linkedid ( struct ast_channel chan)

Check and potentially retire a Linked ID.

Parameters:
chanchannel that is being destroyed or its linkedid is changing
Since:
1.8

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.

Returns:
nothing

Definition at line 429 of file cel.c.

References ao2_find, ao2_ref, ao2_unlink, AST_CEL_LINKEDID_END, ast_cel_report_event(), ast_cel_track_event(), ast_channel_linkedid(), ast_log(), ast_mutex_lock, ast_mutex_unlock, ast_strlen_zero(), cel_linkedid::count, LOG_ERROR, OBJ_KEY, and reload_lock.

Referenced by ast_channel_change_linkedid(), and ast_channel_destructor().

{
   const char *linkedid = ast_channel_linkedid(chan);
   struct cel_linkedid *lid;

   if (ast_strlen_zero(linkedid)) {
      return;
   }

   /* Get the lock in case any CEL events are still in flight when we shutdown. */
   ast_mutex_lock(&reload_lock);

   if (!cel_enabled || !ast_cel_track_event(AST_CEL_LINKEDID_END)
      || !linkedids) {
      /*
       * CEL is disabled or we are not tracking linkedids
       * or the CEL module is shutdown.
       */
      ast_mutex_unlock(&reload_lock);
      return;
   }

   lid = ao2_find(linkedids, (void *) linkedid, OBJ_KEY);
   if (!lid) {
      ast_mutex_unlock(&reload_lock);

      /*
       * The user may have done a reload to start tracking linkedids
       * when a call was already in progress.  This is an unusual kind
       * of change to make after starting Asterisk.
       */
      ast_log(LOG_ERROR, "Something weird happened, couldn't find linkedid %s\n", linkedid);
      return;
   }

   if (!--lid->count) {
      /* No channels use this linkedid anymore. */
      ao2_unlink(linkedids, lid);
      ast_mutex_unlock(&reload_lock);

      ast_cel_report_event(chan, AST_CEL_LINKEDID_END, NULL, NULL, NULL);
   } else {
      ast_mutex_unlock(&reload_lock);
   }
   ao2_ref(lid, -1);
}
int ast_cel_engine_init ( void  )

Provided by cel.c

Definition at line 871 of file cel.c.

References AO2_ALLOC_OPT_LOCK_NOLOCK, ao2_container_alloc_options, app_cmp(), app_hash(), ast_cel_engine_term(), ast_cli_register(), ast_register_atexit(), do_reload(), lid_cmp(), lid_hash(), and NUM_APP_BUCKETS.

Referenced by main().

{
   /*
    * Accesses to the appset and linkedids containers have to be
    * protected by the reload_lock so they don't need a lock of
    * their own.
    */
   appset = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_NOLOCK, NUM_APP_BUCKETS,
      app_hash, app_cmp);
   if (!appset) {
      return -1;
   }
   linkedids = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_NOLOCK, NUM_APP_BUCKETS,
      lid_hash, lid_cmp);
   if (!linkedids) {
      ast_cel_engine_term();
      return -1;
   }

   if (do_reload(0) || ast_cli_register(&cli_status)) {
      ast_cel_engine_term();
      return -1;
   }

   ast_register_atexit(ast_cel_engine_term);

   return 0;
}
int ast_cel_engine_reload ( void  )

Provided by cel.c

Definition at line 900 of file cel.c.

References do_reload().

{
   return do_reload(1);
}
static void ast_cel_engine_term ( void  ) [static]

Definition at line 856 of file cel.c.

References ao2_cleanup, ast_cli_unregister(), ast_mutex_lock, ast_mutex_unlock, and reload_lock.

Referenced by ast_cel_engine_init().

{
   /* Get the lock in case any CEL events are still in flight when we shutdown. */
   ast_mutex_lock(&reload_lock);

   ao2_cleanup(appset);
   appset = NULL;
   ao2_cleanup(linkedids);
   linkedids = NULL;

   ast_mutex_unlock(&reload_lock);

   ast_cli_unregister(&cli_status);
}
struct ast_channel* ast_cel_fabricate_channel_from_event ( const struct ast_event event) [read]

Create a fake channel from data in a CEL event.

Note:
This function creates a fake channel containing the serialized channel data in the given cel event. It should be released with ast_channel_unref() but could be released with ast_channel_release().
Parameters:
eventthe CEL event
Since:
1.8
Returns:
a channel with the data filled in, or NULL on error
Todo:
This function is very expensive, especially given that some CEL backends use it on every CEL event. This function really needs to go away at some point.

Definition at line 484 of file cel.c.

References ast_cel_event_record::account_code, ast_cel_event_record::amaflag, ast_party_caller::ani, ast_cel_event_record::application_data, ast_cel_event_record::application_name, AST_CEL_EVENT_RECORD_VERSION, ast_cel_fill_record(), AST_CEL_USER_DEFINED, ast_channel_amaflags_set(), ast_channel_appl_set(), ast_channel_caller(), ast_channel_context_set(), ast_channel_data_set(), ast_channel_datastore_add(), ast_channel_dialed(), ast_channel_exten_set(), ast_channel_linkedid_set(), ast_channel_name_set(), ast_channel_redirecting(), ast_channel_unref, ast_channel_varshead(), ast_datastore_alloc(), ast_datastore_free(), ast_dummy_channel_alloc(), AST_LIST_INSERT_HEAD, ast_localtime(), ast_malloc, ast_strdup, ast_strftime(), ast_strlen_zero(), ast_var_assign(), ast_cel_event_record::caller_id_ani, ast_cel_event_record::caller_id_dnid, ast_cel_event_record::caller_id_name, ast_cel_event_record::caller_id_num, ast_cel_event_record::caller_id_rdnis, ast_cel_event_record::channel_name, ast_cel_event_record::context, ast_datastore::data, ast_cel_event_record::event_name, ast_cel_event_record::event_time, ast_cel_event_record::event_type, ast_cel_event_record::extension, ast_cel_event_record::extra, ast_party_redirecting::from, ast_party_caller::id, ast_cel_event_record::linked_id, ast_party_id::name, ast_party_id::number, ast_party_dialed::number, ast_cel_event_record::peer, ast_cel_event_record::peer_account, ast_party_name::str, ast_party_number::str, ast_party_dialed::str, ast_cel_event_record::unique_id, ast_cel_event_record::user_defined_name, ast_cel_event_record::user_field, ast_party_name::valid, ast_party_number::valid, and ast_cel_event_record::version.

{
   struct varshead *headp;
   struct ast_var_t *newvariable;
   const char *mixed_name;
   char timebuf[30];
   struct ast_channel *tchan;
   struct ast_cel_event_record record = {
      .version = AST_CEL_EVENT_RECORD_VERSION,
   };
   struct ast_datastore *datastore;
   char *app_data;

   /* do not call ast_channel_alloc because this is not really a real channel */
   if (!(tchan = ast_dummy_channel_alloc())) {
      return NULL;
   }

   headp = ast_channel_varshead(tchan);

   /* first, get the variables from the event */
   if (ast_cel_fill_record(event, &record)) {
      ast_channel_unref(tchan);
      return NULL;
   }

   /* next, fill the channel with their data */
   mixed_name = (record.event_type == AST_CEL_USER_DEFINED)
      ? record.user_defined_name : record.event_name;
   if ((newvariable = ast_var_assign("eventtype", mixed_name))) {
      AST_LIST_INSERT_HEAD(headp, newvariable, entries);
   }

   if (ast_strlen_zero(cel_dateformat)) {
      snprintf(timebuf, sizeof(timebuf), "%ld.%06ld", (long) record.event_time.tv_sec,
            (long) 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("eventenum", record.event_name))) {
      AST_LIST_INSERT_HEAD(headp, newvariable, entries);
   }
   if ((newvariable = ast_var_assign("userdeftype", record.user_defined_name))) {
      AST_LIST_INSERT_HEAD(headp, newvariable, entries);
   }
   if ((newvariable = ast_var_assign("eventextra", record.extra))) {
      AST_LIST_INSERT_HEAD(headp, newvariable, entries);
   }

   ast_channel_caller(tchan)->id.name.valid = 1;
   ast_channel_caller(tchan)->id.name.str = ast_strdup(record.caller_id_name);
   ast_channel_caller(tchan)->id.number.valid = 1;
   ast_channel_caller(tchan)->id.number.str = ast_strdup(record.caller_id_num);
   ast_channel_caller(tchan)->ani.number.valid = 1;
   ast_channel_caller(tchan)->ani.number.str = ast_strdup(record.caller_id_ani);
   ast_channel_redirecting(tchan)->from.number.valid = 1;
   ast_channel_redirecting(tchan)->from.number.str = ast_strdup(record.caller_id_rdnis);
   ast_channel_dialed(tchan)->number.str = ast_strdup(record.caller_id_dnid);

   ast_channel_exten_set(tchan, record.extension);
   ast_channel_context_set(tchan, record.context);
   ast_channel_name_set(tchan, record.channel_name);
   ast_channel_uniqueid_set(tchan, record.unique_id);
   ast_channel_linkedid_set(tchan, record.linked_id);
   ast_channel_accountcode_set(tchan, record.account_code);
   ast_channel_peeraccount_set(tchan, record.peer_account);
   ast_channel_userfield_set(tchan, record.user_field);

   if ((newvariable = ast_var_assign("BRIDGEPEER", record.peer))) {
      AST_LIST_INSERT_HEAD(headp, newvariable, entries);
   }

   ast_channel_amaflags_set(tchan, record.amaflag);

   /* We need to store an 'application name' and 'application
    * data' on the channel for logging purposes, but the channel
    * structure only provides a place to store pointers, and it
    * expects these pointers to be pointing to data that does not
    * need to be freed. This means that the channel's destructor
    * does not attempt to free any storage that these pointers
    * point to. However, we can't provide data in that form directly for
    * these structure members. In order to ensure that these data
    * elements have a lifetime that matches the channel's
    * lifetime, we'll put them in a datastore attached to the
    * channel, and set's the channel's pointers to point into the
    * datastore.  The datastore will then be automatically destroyed
    * when the channel is destroyed.
    */

   if (!(datastore = ast_datastore_alloc(&fabricated_channel_datastore, NULL))) {
      ast_channel_unref(tchan);
      return NULL;
   }

   if (!(app_data = ast_malloc(strlen(record.application_name) + strlen(record.application_data) + 2))) {
      ast_datastore_free(datastore);
      ast_channel_unref(tchan);
      return NULL;
   }

   ast_channel_appl_set(tchan, strcpy(app_data, record.application_name));
   ast_channel_data_set(tchan, strcpy(app_data + strlen(record.application_name) + 1,
      record.application_data));

   datastore->data = app_data;
   ast_channel_datastore_add(tchan, datastore);

   return tchan;
}
int ast_cel_fill_record ( const struct ast_event event,
struct ast_cel_event_record r 
)

Fill in an ast_cel_event_record from a CEL event.

Parameters:
[in]eventthe CEL event
[out]rthe ast_cel_event_record to fill in
Since:
1.8
Return values:
0success
non-zerofailure

Definition at line 765 of file cel.c.

References ast_cel_event_record::account_code, ast_cel_event_record::amaflag, ast_cel_event_record::application_data, ast_cel_event_record::application_name, AST_CEL_EVENT_RECORD_VERSION, ast_cel_get_type_name(), AST_CEL_USER_DEFINED, ast_event_get_ie_str(), ast_event_get_ie_uint(), AST_EVENT_IE_CEL_ACCTCODE, AST_EVENT_IE_CEL_AMAFLAGS, AST_EVENT_IE_CEL_APPDATA, AST_EVENT_IE_CEL_APPNAME, AST_EVENT_IE_CEL_CHANNAME, AST_EVENT_IE_CEL_CIDANI, AST_EVENT_IE_CEL_CIDDNID, AST_EVENT_IE_CEL_CIDNAME, AST_EVENT_IE_CEL_CIDNUM, AST_EVENT_IE_CEL_CIDRDNIS, AST_EVENT_IE_CEL_CONTEXT, AST_EVENT_IE_CEL_EVENT_TIME, AST_EVENT_IE_CEL_EVENT_TIME_USEC, AST_EVENT_IE_CEL_EVENT_TYPE, AST_EVENT_IE_CEL_EXTEN, AST_EVENT_IE_CEL_EXTRA, AST_EVENT_IE_CEL_LINKEDID, AST_EVENT_IE_CEL_PEER, AST_EVENT_IE_CEL_UNIQUEID, AST_EVENT_IE_CEL_USEREVENT_NAME, AST_EVENT_IE_CEL_USERFIELD, ast_log(), ast_cel_event_record::caller_id_ani, ast_cel_event_record::caller_id_dnid, ast_cel_event_record::caller_id_name, ast_cel_event_record::caller_id_num, ast_cel_event_record::caller_id_rdnis, ast_cel_event_record::channel_name, ast_cel_event_record::context, ast_cel_event_record::event_name, ast_cel_event_record::event_time, ast_cel_event_record::event_type, ast_cel_event_record::extension, ast_cel_event_record::extra, ast_cel_event_record::linked_id, LOG_ERROR, ast_cel_event_record::peer, ast_cel_event_record::peer_account, S_OR, ast_cel_event_record::unique_id, ast_cel_event_record::user_defined_name, ast_cel_event_record::user_field, and ast_cel_event_record::version.

Referenced by ast_cel_fabricate_channel_from_event().

{
   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->event_name = ast_cel_get_type_name(r->event_type);
   if (r->event_type == AST_CEL_USER_DEFINED) {
      r->user_defined_name = ast_event_get_ie_str(e, AST_EVENT_IE_CEL_USEREVENT_NAME);
   } else {
      r->user_defined_name = "";
   }

   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;
}
const char* ast_cel_get_ama_flag_name ( enum ast_cel_ama_flag  flag)

Convert AMA flag to printable string.

Parameters:
[in]flagthe flag to convert to a string
Since:
1.8
Returns:
the string representation of the flag

Definition at line 417 of file cel.c.

References ARRAY_LEN, ast_log(), LOG_WARNING, and S_OR.

{
   if (flag < 0 || flag >= ARRAY_LEN(cel_ama_flags)) {
      ast_log(LOG_WARNING, "Invalid AMA flag: %d\n", flag);
      return "Unknown";
   }

   return S_OR(cel_ama_flags[flag], "Unknown");
}
const char* ast_cel_get_type_name ( enum ast_cel_event_type  type)

Get the name of a CEL event type.

Parameters:
typethe type to get the name of
Since:
1.8
Returns:
the string representation of the type

Definition at line 412 of file cel.c.

References S_OR.

Referenced by ast_cel_fill_record(), and handle_cli_status().

{
   return S_OR(cel_event_types[type], "Unknown");
}
int ast_cel_linkedid_ref ( const char *  linkedid)

Inform CEL that a new linkedid is being used.

Since:
11
Return values:
-1error
0success

Definition at line 601 of file cel.c.

References AO2_ALLOC_OPT_LOCK_NOLOCK, ao2_alloc_options, ao2_find, ao2_link, ao2_ref, AST_CEL_LINKEDID_END, ast_cel_track_event(), ast_log(), ast_mutex_lock, ast_mutex_unlock, ast_strlen_zero(), cel_linkedid::count, cel_linkedid::id, LOG_ERROR, OBJ_KEY, and reload_lock.

Referenced by ast_cel_report_event(), and ast_channel_change_linkedid().

{
   struct cel_linkedid *lid;

   if (ast_strlen_zero(linkedid)) {
      ast_log(LOG_ERROR, "The linkedid should never be empty\n");
      return -1;
   }

   /* Get the lock in case any CEL events are still in flight when we shutdown. */
   ast_mutex_lock(&reload_lock);

   if (!cel_enabled || !ast_cel_track_event(AST_CEL_LINKEDID_END)) {
      /* CEL is disabled or we are not tracking linkedids. */
      ast_mutex_unlock(&reload_lock);
      return 0;
   }
   if (!linkedids) {
      /* The CEL module is shutdown.  Abort. */
      ast_mutex_unlock(&reload_lock);
      return -1;
   }

   lid = ao2_find(linkedids, (void *) linkedid, OBJ_KEY);
   if (!lid) {
      /*
       * Changes to the lid->count member are protected by the
       * reload_lock so the lid object does not need its own lock.
       */
      lid = ao2_alloc_options(sizeof(*lid) + strlen(linkedid) + 1, NULL,
         AO2_ALLOC_OPT_LOCK_NOLOCK);
      if (!lid) {
         ast_mutex_unlock(&reload_lock);
         return -1;
      }
      strcpy(lid->id, linkedid);/* Safe */

      ao2_link(linkedids, lid);
   }
   ++lid->count;
   ast_mutex_unlock(&reload_lock);
   ao2_ref(lid, -1);

   return 0;
}
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 
)

Report a channel event.

Parameters:
chanThis argument is required. This is the primary channel associated with this channel event.
event_typeThis is the type of call event being reported.
userdefevnameThis is an optional custom name for the call event.
extraThis is an optional opaque field that will go into the "CEL_EXTRA" information element of the call event.
peer2All 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.8
Precondition:
chan and peer2 are both unlocked
Return values:
0success
non-zerofailure

Definition at line 647 of file cel.c.

References ao2_find, ao2_ref, app, ast_bridged_channel(), AST_CEL_APP_END, AST_CEL_APP_START, AST_CEL_CHANNEL_START, AST_CEL_LINKEDID_END, ast_cel_linkedid_ref(), ast_cel_track_event(), ast_channel_accountcode(), ast_channel_amaflags(), ast_channel_appl(), ast_channel_caller(), ast_channel_context(), ast_channel_data(), ast_channel_dialed(), ast_channel_exten(), ast_channel_linkedid(), ast_channel_lock, ast_channel_name(), ast_channel_peeraccount(), ast_channel_redirecting(), ast_channel_ref, ast_channel_uniqueid(), ast_channel_unlock, ast_channel_unref, ast_channel_userfield(), AST_EVENT_CEL, ast_event_destroy(), AST_EVENT_IE_CEL_ACCTCODE, AST_EVENT_IE_CEL_AMAFLAGS, AST_EVENT_IE_CEL_APPDATA, AST_EVENT_IE_CEL_APPNAME, AST_EVENT_IE_CEL_CHANNAME, AST_EVENT_IE_CEL_CIDANI, AST_EVENT_IE_CEL_CIDDNID, AST_EVENT_IE_CEL_CIDNAME, AST_EVENT_IE_CEL_CIDNUM, AST_EVENT_IE_CEL_CIDRDNIS, AST_EVENT_IE_CEL_CONTEXT, AST_EVENT_IE_CEL_EVENT_TIME, AST_EVENT_IE_CEL_EVENT_TIME_USEC, AST_EVENT_IE_CEL_EVENT_TYPE, AST_EVENT_IE_CEL_EXTEN, AST_EVENT_IE_CEL_EXTRA, AST_EVENT_IE_CEL_LINKEDID, AST_EVENT_IE_CEL_PEER, AST_EVENT_IE_CEL_PEERACCT, AST_EVENT_IE_CEL_UNIQUEID, AST_EVENT_IE_CEL_USEREVENT_NAME, AST_EVENT_IE_CEL_USERFIELD, AST_EVENT_IE_END, AST_EVENT_IE_PLTYPE_STR, AST_EVENT_IE_PLTYPE_UINT, ast_event_new(), ast_event_queue(), ast_mutex_lock, ast_mutex_unlock, ast_tvnow(), ast_channel::linkedid, name, OBJ_POINTER, reload_lock, S_COR, and S_OR.

Referenced by __ast_channel_alloc_ap(), __ast_read(), analog_attempt_transfer(), ast_bridge_call(), ast_cel_check_retire_linkedid(), ast_channel_destructor(), ast_do_masquerade(), ast_do_pickup(), ast_hangup(), ast_raw_answer(), builtin_atxfer(), builtin_blindtransfer(), celgenuserevent_exec(), do_forward(), handle_request_refer(), local_attended_transfer(), manage_parked_call(), park_call_full(), parked_call_exec(), pbx_exec(), and wait_for_answer().

{
   struct timeval eventtime;
   struct ast_event *ev;
   const char *peername = "";
   struct ast_channel *peer;
   char *linkedid = ast_strdupa(ast_channel_linkedid(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 (!appset) {
      /* The CEL module is shutdown.  Abort. */
      ast_mutex_unlock(&reload_lock);
      return -1;
   }

   /* Record the linkedid of new channels if we are tracking LINKEDID_END even if we aren't
    * reporting on CHANNEL_START so we can track when to send LINKEDID_END */
   if (cel_enabled && ast_cel_track_event(AST_CEL_LINKEDID_END) && event_type == AST_CEL_CHANNEL_START && linkedid) {
      if (ast_cel_linkedid_ref(linkedid)) {
         ast_mutex_unlock(&reload_lock);
         return -1;
      }
   }

   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 *) ast_channel_appl(chan), OBJ_POINTER))) {
         ast_mutex_unlock(&reload_lock);
         return 0;
      }
      ao2_ref(app, -1);
   }

   ast_mutex_unlock(&reload_lock);

   ast_channel_lock(chan);
   peer = ast_bridged_channel(chan);
   if (peer) {
      ast_channel_ref(peer);
   }
   ast_channel_unlock(chan);

   if (peer) {
      ast_channel_lock(peer);
      peername = ast_strdupa(ast_channel_name(peer));
      ast_channel_unlock(peer);
   } else if (peer2) {
      ast_channel_lock(peer2);
      peername = ast_strdupa(ast_channel_name(peer2));
      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_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, ""),
      AST_EVENT_IE_CEL_CIDNUM, AST_EVENT_IE_PLTYPE_STR,
         S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, ""),
      AST_EVENT_IE_CEL_CIDANI, AST_EVENT_IE_PLTYPE_STR,
         S_COR(ast_channel_caller(chan)->ani.number.valid, ast_channel_caller(chan)->ani.number.str, ""),
      AST_EVENT_IE_CEL_CIDRDNIS, AST_EVENT_IE_PLTYPE_STR,
         S_COR(ast_channel_redirecting(chan)->from.number.valid, ast_channel_redirecting(chan)->from.number.str, ""),
      AST_EVENT_IE_CEL_CIDDNID, AST_EVENT_IE_PLTYPE_STR,
         S_OR(ast_channel_dialed(chan)->number.str, ""),
      AST_EVENT_IE_CEL_EXTEN, AST_EVENT_IE_PLTYPE_STR, ast_channel_exten(chan),
      AST_EVENT_IE_CEL_CONTEXT, AST_EVENT_IE_PLTYPE_STR, ast_channel_context(chan),
      AST_EVENT_IE_CEL_CHANNAME, AST_EVENT_IE_PLTYPE_STR, ast_channel_name(chan),
      AST_EVENT_IE_CEL_APPNAME, AST_EVENT_IE_PLTYPE_STR, S_OR(ast_channel_appl(chan), ""),
      AST_EVENT_IE_CEL_APPDATA, AST_EVENT_IE_PLTYPE_STR, S_OR(ast_channel_data(chan), ""),
      AST_EVENT_IE_CEL_AMAFLAGS, AST_EVENT_IE_PLTYPE_UINT, ast_channel_amaflags(chan),
      AST_EVENT_IE_CEL_ACCTCODE, AST_EVENT_IE_PLTYPE_STR, ast_channel_accountcode(chan),
      AST_EVENT_IE_CEL_PEERACCT, AST_EVENT_IE_PLTYPE_STR, ast_channel_peeraccount(chan),
      AST_EVENT_IE_CEL_UNIQUEID, AST_EVENT_IE_PLTYPE_STR, ast_channel_uniqueid(chan),
      AST_EVENT_IE_CEL_LINKEDID, AST_EVENT_IE_PLTYPE_STR, ast_channel_linkedid(chan),
      AST_EVENT_IE_CEL_USERFIELD, AST_EVENT_IE_PLTYPE_STR, ast_channel_userfield(chan),
      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;
}
enum ast_cel_event_type ast_cel_str_to_event_type ( const char *  name)

Get the event type from a string.

Parameters:
namethe event type name as a string
Since:
1.8
Returns:
the ast_cel_event_type given by the string

Definition at line 254 of file cel.c.

References ARRAY_LEN.

Referenced by parse_events().

{
   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) [static]

Definition at line 271 of file cel.c.

Referenced by ast_cel_check_retire_linkedid(), ast_cel_linkedid_ref(), ast_cel_report_event(), and parse_apps().

{
   return (eventset & ((int64_t) 1 << et));
}
static int do_reload ( int  is_reload) [static]

Definition at line 342 of file cel.c.

References ast_config_destroy(), ast_config_load2(), ast_copy_string(), ast_log(), ast_mutex_lock, ast_mutex_unlock, ast_true(), ast_variable_retrieve(), ast_verb, config, CONFIG_STATUS_FILEINVALID, CONFIG_STATUS_FILEUNCHANGED, LOG_WARNING, parse_apps(), parse_events(), reload_lock, and set_defaults().

Referenced by ast_cel_engine_init(), and ast_cel_engine_reload().

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

   if (!is_reload) {
      /* Initialize all settings before first configuration load. */
      set_defaults();
   }

   /*
    * Unfortunately we have to always load the config file because
    * other modules read the same file.
    */
   config = ast_config_load2(cel_conf_file, "cel", config_flags);
   if (!config || config == CONFIG_STATUS_FILEINVALID) {
      ast_log(LOG_WARNING, "Could not load %s\n", cel_conf_file);
      config = NULL;
      goto return_cleanup;
   }
   if (config == CONFIG_STATUS_FILEUNCHANGED) {
      /* This should never happen because we always load the config file. */
      config = NULL;
      goto return_cleanup;
   }

   if (is_reload) {
      /* Reset all settings before reloading configuration */
      set_defaults();
   }

   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;
}
static char* handle_cli_status ( struct ast_cli_entry e,
int  cmd,
struct ast_cli_args a 
) [static]

Definition at line 177 of file cel.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_ref, app, ast_cli_args::argc, ast_cel_get_type_name(), ast_cli(), AST_EVENT_CEL, AST_EVENT_IE_EVENTTYPE, ast_event_report_subs(), AST_EVENT_SUB, ast_event_sub_append_ie_uint(), ast_event_sub_destroy(), ast_event_subscribe_new(), ast_mutex_lock, ast_mutex_unlock, CLI_FAILURE, CLI_GENERATE, CLI_HANDLER, CLI_INIT, CLI_SHOWUSAGE, CLI_SUCCESS, ast_cli_entry::command, eventset, ast_cli_args::fd, name, print_cel_sub(), reload_lock, sub, and ast_cli_entry::usage.

{
   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);
      }
   }

   /* Accesses to the appset container must be done while holding the reload_lock. */
   ast_mutex_lock(&reload_lock);
   if (appset) {
      struct ao2_iterator iter;
      char *app;

      iter = ao2_iterator_init(appset, 0);
      for (;;) {
         app = ao2_iterator_next(&iter);
         if (!app) {
            break;
         }
         ast_mutex_unlock(&reload_lock);

         ast_cli(a->fd, "CEL Tracking Application: %s\n", app);

         ao2_ref(app, -1);
         ast_mutex_lock(&reload_lock);
      }
      ao2_iterator_destroy(&iter);
   }
   ast_mutex_unlock(&reload_lock);

   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 int lid_cmp ( void *  obj,
void *  arg,
int  flags 
) [static]

Definition at line 838 of file cel.c.

References CMP_MATCH, cel_linkedid::id, OBJ_KEY, and OBJ_POINTER.

Referenced by ast_cel_engine_init().

{
   struct cel_linkedid *lid1 = obj;
   struct cel_linkedid *lid2 = arg;
   const char *key = arg;

   switch (flags & (OBJ_POINTER | OBJ_KEY)) {
   case OBJ_POINTER:
   default:
      key = lid2->id;
      break;
   case OBJ_KEY:
      break;
   }

   return !strcasecmp(lid1->id, key) ? CMP_MATCH : 0;
}
static int lid_hash ( const void *  obj,
const int  flags 
) [static]

Definition at line 821 of file cel.c.

References ast_str_case_hash(), cel_linkedid::id, OBJ_KEY, and OBJ_POINTER.

Referenced by ast_cel_engine_init().

{
   const struct cel_linkedid *lid = obj;
   const char *key = obj;

   switch (flags & (OBJ_POINTER | OBJ_KEY)) {
   case OBJ_POINTER:
   default:
      key = lid->id;
      break;
   case OBJ_KEY:
      break;
   }

   return ast_str_case_hash(key);
}
static void parse_apps ( const char *  val) [static]

Definition at line 303 of file cel.c.

References AO2_ALLOC_OPT_LOCK_NOLOCK, ao2_alloc_options, ao2_link, ao2_ref, app, AST_CEL_APP_END, AST_CEL_APP_START, ast_cel_track_event(), ast_log(), ast_strip(), ast_strlen_zero(), and LOG_WARNING.

Referenced by do_reload().

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

      /* The app object is immutable so it doesn't need a lock of its own. */
      app = ao2_alloc_options(strlen(cur_app) + 1, NULL, AO2_ALLOC_OPT_LOCK_NOLOCK);
      if (!app) {
         continue;
      }
      strcpy(app, cur_app);/* Safe */

      ao2_link(appset, app);
      ao2_ref(app, -1);
      app = NULL;
   }
}
static void parse_events ( const char *  val) [static]

Definition at line 276 of file cel.c.

References ast_cel_str_to_event_type(), ast_log(), ast_strip(), ast_strlen_zero(), events, and LOG_WARNING.

Referenced by do_reload().

{
   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 print_cel_sub ( const struct ast_event event,
void *  data 
) [static]

Definition at line 169 of file cel.c.

References ast_cli(), ast_event_get_ie_str(), AST_EVENT_IE_DESCRIPTION, and ast_cli_args::fd.

Referenced by handle_cli_status().

{
   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 void set_defaults ( void  ) [static]

Variable Documentation

struct ao2_container* appset [static]

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.

Note:
Accesses to the appset container must be done while holding the reload_lock.

Definition at line 100 of file cel.c.

const char* const cel_ama_flags[AST_CEL_AMA_FLAG_TOTAL] [static]

Map of ast_cel_ama_flags to strings.

Definition at line 157 of file cel.c.

const char cel_conf_file[] = "cel.conf" [static]

Config file to load for the CEL feature.

Definition at line 51 of file cel.c.

char cel_dateformat[256] [static]

Configured date format for event timestamps.

Definition at line 120 of file cel.c.

unsigned char cel_enabled [static]

Is the CEL subsystem enabled ?

Definition at line 54 of file cel.c.

Referenced by ast_cel_check_enabled().

const char* const cel_event_types[CEL_MAX_EVENT_IDS] [static]

Map of ast_cel_event_type to strings.

Definition at line 125 of file cel.c.

struct ast_cli_entry cli_status = AST_CLI_DEFINE(handle_cli_status, "Display the CEL status") [static]

Definition at line 252 of file cel.c.

int64_t eventset [static]

which events we want to track

Note:
bit field, up to 64 events

Definition at line 64 of file cel.c.

Referenced by handle_cli_status().

Initial value:
 {
   .type = "CEL fabricated channel",
   .destroy = ast_free_ptr,
}

Definition at line 479 of file cel.c.

struct ao2_container* linkedids [static]

Container of channel references to a linkedid for CEL purposes.

Note:
Accesses to the linkedids container must be done while holding the reload_lock.

Definition at line 115 of file cel.c.

ast_mutex_t reload_lock = { PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP , NULL, 1 } [static]

Lock protecting CEL.

Note:
It protects during reloads, shutdown, and accesses to the appset and linkedids containers.

Definition at line 88 of file cel.c.

Referenced by ast_cel_check_retire_linkedid(), ast_cel_engine_term(), ast_cel_linkedid_ref(), ast_cel_report_event(), do_reload(), and handle_cli_status().