cel.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 2007 - 2009, Digium, Inc.
00005  *
00006  * See http://www.asterisk.org for more information about
00007  * the Asterisk project. Please do not directly contact
00008  * any of the maintainers of this project for assistance;
00009  * the project provides a web site, mailing lists and IRC
00010  * channels for your use.
00011  *
00012  * This program is free software, distributed under the terms of
00013  * the GNU General Public License Version 2. See the LICENSE file
00014  * at the top of the source tree.
00015  */
00016 
00017 /*!
00018  * \file
00019  *
00020  * \brief Channel Event Logging API
00021  *
00022  * \author Steve Murphy <murf@digium.com>
00023  * \author Russell Bryant <russell@digium.com>
00024  */
00025 
00026 /*! \li \ref cel.c uses the configuration file \ref cel.conf
00027  * \addtogroup configuration_file Configuration Files
00028  */
00029 
00030 /*!
00031  * \page cel.conf cel.conf
00032  * \verbinclude cel.conf.sample
00033  */
00034 
00035 /*** MODULEINFO
00036    <support_level>core</support_level>
00037  ***/
00038 
00039 #include "asterisk.h"
00040 
00041 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 434471 $")
00042 
00043 #include "asterisk/_private.h"
00044 
00045 #include "asterisk/channel.h"
00046 #include "asterisk/pbx.h"
00047 #include "asterisk/cel.h"
00048 #include "asterisk/logger.h"
00049 #include "asterisk/linkedlists.h"
00050 #include "asterisk/utils.h"
00051 #include "asterisk/config.h"
00052 #include "asterisk/config_options.h"
00053 #include "asterisk/cli.h"
00054 #include "asterisk/astobj2.h"
00055 #include "asterisk/stasis_message_router.h"
00056 #include "asterisk/stasis_channels.h"
00057 #include "asterisk/stasis_bridges.h"
00058 #include "asterisk/bridge.h"
00059 #include "asterisk/parking.h"
00060 #include "asterisk/pickup.h"
00061 #include "asterisk/core_local.h"
00062 
00063 /*** DOCUMENTATION
00064    <configInfo name="cel" language="en_US">
00065       <configFile name="cel.conf">
00066          <configObject name="general">
00067             <synopsis>Options that apply globally to Channel Event Logging (CEL)</synopsis>
00068             <configOption name="enable">
00069                <synopsis>Determines whether CEL is enabled</synopsis>
00070             </configOption>
00071             <configOption name="dateformat">
00072                <synopsis>The format to be used for dates when logging</synopsis>
00073             </configOption>
00074             <configOption name="apps">
00075                <synopsis>List of apps for CEL to track</synopsis>
00076                <description><para>A case-insensitive, comma-separated list of applications
00077                to track when one or both of APP_START and APP_END events are flagged for
00078                tracking</para></description>
00079             </configOption>
00080             <configOption name="events">
00081                <synopsis>List of events for CEL to track</synopsis>
00082                <description><para>A case-sensitive, comma-separated list of event names
00083                to track. These event names do not include the leading <literal>AST_CEL</literal>.
00084                </para>
00085                <enumlist>
00086                   <enum name="ALL">
00087                      <para>Special value which tracks all events.</para>
00088                   </enum>
00089                   <enum name="CHAN_START"/>
00090                   <enum name="CHAN_END"/>
00091                   <enum name="ANSWER"/>
00092                   <enum name="HANGUP"/>
00093                   <enum name="APP_START"/>
00094                   <enum name="APP_END"/>
00095                   <enum name="PARK_START"/>
00096                   <enum name="PARK_END"/>
00097                   <enum name="USER_DEFINED"/>
00098                   <enum name="BRIDGE_ENTER"/>
00099                   <enum name="BRIDGE_EXIT"/>
00100                   <enum name="BLINDTRANSFER"/>
00101                   <enum name="ATTENDEDTRANSFER"/>
00102                   <enum name="PICKUP"/>
00103                   <enum name="FORWARD"/>
00104                   <enum name="LINKEDID_END"/>
00105                   <enum name="LOCAL_OPTIMIZE"/>
00106                </enumlist>
00107                </description>
00108             </configOption>
00109          </configObject>
00110       </configFile>
00111    </configInfo>
00112  ***/
00113 
00114 /*! Message router for state that CEL needs to know about */
00115 static struct stasis_message_router *cel_state_router;
00116 
00117 /*! Topic for CEL-specific messages */
00118 static struct stasis_topic *cel_topic;
00119 
00120 /*! Aggregation topic for all topics CEL needs to know about */
00121 static struct stasis_topic *cel_aggregation_topic;
00122 
00123 /*! Subscription for forwarding the channel caching topic */
00124 static struct stasis_forward *cel_channel_forwarder;
00125 
00126 /*! Subscription for forwarding the channel caching topic */
00127 static struct stasis_forward *cel_bridge_forwarder;
00128 
00129 /*! Subscription for forwarding the parking topic */
00130 static struct stasis_forward *cel_parking_forwarder;
00131 
00132 /*! Subscription for forwarding the CEL-specific topic */
00133 static struct stasis_forward *cel_cel_forwarder;
00134 
00135 struct stasis_message_type *cel_generic_type(void);
00136 STASIS_MESSAGE_TYPE_DEFN(cel_generic_type);
00137 
00138 /*! Container for CEL backend information */
00139 static AO2_GLOBAL_OBJ_STATIC(cel_backends);
00140 
00141 /*! The number of buckets into which backend names will be hashed */
00142 #define BACKEND_BUCKETS 13
00143 
00144 /*! Container for dial end multichannel blobs for holding on to dial statuses */
00145 static AO2_GLOBAL_OBJ_STATIC(cel_dialstatus_store);
00146 
00147 /*!
00148  * \brief Maximum possible CEL event IDs
00149  * \note This limit is currently imposed by the eventset definition
00150  */
00151 #define CEL_MAX_EVENT_IDS 64
00152 
00153 /*!
00154  * \brief Number of buckets for the appset container
00155  */
00156 #define NUM_APP_BUCKETS    97
00157 
00158 /*!
00159  * \brief Number of buckets for the dialstatus container
00160  */
00161 #define NUM_DIALSTATUS_BUCKETS   251
00162 
00163 struct cel_linkedid {
00164    /*! Number of channels with this linkedid. */
00165    unsigned int count;
00166    /*! Linkedid stored at end of struct. */
00167    char id[0];
00168 };
00169 
00170 /*! Container of channel references to a linkedid for CEL purposes. */
00171 static AO2_GLOBAL_OBJ_STATIC(cel_linkedids);
00172 
00173 /*! \brief Destructor for cel_config */
00174 static void cel_general_config_dtor(void *obj)
00175 {
00176    struct ast_cel_general_config *cfg = obj;
00177    ast_string_field_free_memory(cfg);
00178    ao2_cleanup(cfg->apps);
00179    cfg->apps = NULL;
00180 }
00181 
00182 void *ast_cel_general_config_alloc(void)
00183 {
00184    RAII_VAR(struct ast_cel_general_config *, cfg, NULL, ao2_cleanup);
00185 
00186    if (!(cfg = ao2_alloc(sizeof(*cfg), cel_general_config_dtor))) {
00187       return NULL;
00188    }
00189 
00190    if (ast_string_field_init(cfg, 64)) {
00191       return NULL;
00192    }
00193 
00194    if (!(cfg->apps = ast_str_container_alloc(NUM_APP_BUCKETS))) {
00195       return NULL;
00196    }
00197 
00198    ao2_ref(cfg, +1);
00199    return cfg;
00200 }
00201 
00202 /*! \brief A container that holds all config-related information */
00203 struct cel_config {
00204    struct ast_cel_general_config *general;
00205 };
00206 
00207 
00208 static AO2_GLOBAL_OBJ_STATIC(cel_configs);
00209 
00210 /*! \brief Destructor for cel_config */
00211 static void cel_config_dtor(void *obj)
00212 {
00213    struct cel_config *cfg = obj;
00214    ao2_cleanup(cfg->general);
00215    cfg->general = NULL;
00216 }
00217 
00218 static void *cel_config_alloc(void)
00219 {
00220    RAII_VAR(struct cel_config *, cfg, NULL, ao2_cleanup);
00221 
00222    if (!(cfg = ao2_alloc(sizeof(*cfg), cel_config_dtor))) {
00223       return NULL;
00224    }
00225 
00226    if (!(cfg->general = ast_cel_general_config_alloc())) {
00227       return NULL;
00228    }
00229 
00230    ao2_ref(cfg, +1);
00231    return cfg;
00232 }
00233 
00234 /*! \brief An aco_type structure to link the "general" category to the ast_cel_general_config type */
00235 static struct aco_type general_option = {
00236    .type = ACO_GLOBAL,
00237    .name = "general",
00238    .item_offset = offsetof(struct cel_config, general),
00239    .category_match = ACO_WHITELIST,
00240    .category = "^general$",
00241 };
00242 
00243 /*! \brief The config file to be processed for the module. */
00244 static struct aco_file cel_conf = {
00245    .filename = "cel.conf",                  /*!< The name of the config file */
00246    .types = ACO_TYPES(&general_option),     /*!< The mapping object types to be processed */
00247    .skip_category = "(^manager$|^radius$)", /*!< Config sections used by existing modules. Do not add to this list. */
00248 };
00249 
00250 static int cel_pre_apply_config(void);
00251 
00252 CONFIG_INFO_CORE("cel", cel_cfg_info, cel_configs, cel_config_alloc,
00253    .files = ACO_FILES(&cel_conf),
00254    .pre_apply_config = cel_pre_apply_config,
00255 );
00256 
00257 static int cel_pre_apply_config(void)
00258 {
00259    struct cel_config *cfg = aco_pending_config(&cel_cfg_info);
00260 
00261    if (!cfg->general) {
00262       return -1;
00263    }
00264 
00265    if (!ao2_container_count(cfg->general->apps)) {
00266       return 0;
00267    }
00268 
00269    if (cfg->general->events & ((int64_t) 1 << AST_CEL_APP_START)) {
00270       return 0;
00271    }
00272 
00273    if (cfg->general->events & ((int64_t) 1 << AST_CEL_APP_END)) {
00274       return 0;
00275    }
00276 
00277    ast_log(LOG_ERROR, "Applications are listed to be tracked, but APP events are not tracked\n");
00278    return -1;
00279 }
00280 
00281 static struct aco_type *general_options[] = ACO_TYPES(&general_option);
00282 
00283 /*!
00284  * \brief Map of ast_cel_event_type to strings
00285  */
00286 static const char * const cel_event_types[CEL_MAX_EVENT_IDS] = {
00287    [AST_CEL_ALL]              = "ALL",
00288    [AST_CEL_CHANNEL_START]    = "CHAN_START",
00289    [AST_CEL_CHANNEL_END]      = "CHAN_END",
00290    [AST_CEL_ANSWER]           = "ANSWER",
00291    [AST_CEL_HANGUP]           = "HANGUP",
00292    [AST_CEL_APP_START]        = "APP_START",
00293    [AST_CEL_APP_END]          = "APP_END",
00294    [AST_CEL_PARK_START]       = "PARK_START",
00295    [AST_CEL_PARK_END]         = "PARK_END",
00296    [AST_CEL_USER_DEFINED]     = "USER_DEFINED",
00297    [AST_CEL_BRIDGE_ENTER]     = "BRIDGE_ENTER",
00298    [AST_CEL_BRIDGE_EXIT]      = "BRIDGE_EXIT",
00299    [AST_CEL_BLINDTRANSFER]    = "BLINDTRANSFER",
00300    [AST_CEL_ATTENDEDTRANSFER] = "ATTENDEDTRANSFER",
00301    [AST_CEL_PICKUP]           = "PICKUP",
00302    [AST_CEL_FORWARD]          = "FORWARD",
00303    [AST_CEL_LINKEDID_END]     = "LINKEDID_END",
00304    [AST_CEL_LOCAL_OPTIMIZE]   = "LOCAL_OPTIMIZE",
00305 };
00306 
00307 struct cel_backend {
00308    ast_cel_backend_cb callback; /*!< Callback for this backend */
00309    char name[0];                /*!< Name of this backend */
00310 };
00311 
00312 /*! \brief Hashing function for cel_backend */
00313 static int cel_backend_hash(const void *obj, int flags)
00314 {
00315    const struct cel_backend *backend;
00316    const char *name;
00317 
00318    switch (flags & OBJ_SEARCH_MASK) {
00319    case OBJ_SEARCH_OBJECT:
00320       backend = obj;
00321       name = backend->name;
00322       break;
00323    case OBJ_SEARCH_KEY:
00324       name = obj;
00325       break;
00326    default:
00327       /* Hash can only work on something with a full key. */
00328       ast_assert(0);
00329       return 0;
00330    }
00331 
00332    return ast_str_hash(name);
00333 }
00334 
00335 /*! \brief Comparator function for cel_backend */
00336 static int cel_backend_cmp(void *obj, void *arg, int flags)
00337 {
00338    const struct cel_backend *object_left = obj;
00339    const struct cel_backend *object_right = arg;
00340    const char *right_key = arg;
00341    int cmp;
00342 
00343    switch (flags & OBJ_SEARCH_MASK) {
00344    case OBJ_SEARCH_OBJECT:
00345       right_key = object_right->name;
00346       /* Fall through */
00347    case OBJ_SEARCH_KEY:
00348       cmp = strcmp(object_left->name, right_key);
00349       break;
00350    case OBJ_SEARCH_PARTIAL_KEY:
00351       /*
00352        * We could also use a partial key struct containing a length
00353        * so strlen() does not get called for every comparison instead.
00354        */
00355       cmp = strncmp(object_left->name, right_key, strlen(right_key));
00356       break;
00357    default:
00358       /*
00359        * What arg points to is specific to this traversal callback
00360        * and has no special meaning to astobj2.
00361        */
00362       cmp = 0;
00363       break;
00364    }
00365    if (cmp) {
00366       return 0;
00367    }
00368    /*
00369     * At this point the traversal callback is identical to a sorted
00370     * container.
00371     */
00372    return CMP_MATCH;
00373 }
00374 
00375 static const char *get_caller_uniqueid(struct ast_multi_channel_blob *blob)
00376 {
00377    struct ast_channel_snapshot *caller = ast_multi_channel_blob_get_channel(blob, "caller");
00378    if (!caller) {
00379       return NULL;
00380    }
00381 
00382    return caller->uniqueid;
00383 }
00384 
00385 /*! \brief Hashing function for dialstatus container */
00386 static int dialstatus_hash(const void *obj, int flags)
00387 {
00388    struct ast_multi_channel_blob *blob;
00389    const char *key;
00390 
00391    switch (flags & OBJ_SEARCH_MASK) {
00392    case OBJ_SEARCH_KEY:
00393       key = obj;
00394       break;
00395    case OBJ_SEARCH_OBJECT:
00396       blob = (void *) obj;
00397       key = get_caller_uniqueid(blob);
00398       break;
00399    default:
00400       /* Hash can only work on something with a full key. */
00401       ast_assert(0);
00402       return 0;
00403    }
00404    return ast_str_hash(key);
00405 }
00406 
00407 /*! \brief Comparator function for dialstatus container */
00408 static int dialstatus_cmp(void *obj, void *arg, int flags)
00409 {
00410    struct ast_multi_channel_blob *object_left = obj;
00411    struct ast_multi_channel_blob *object_right = arg;
00412    const char *right_key = arg;
00413    int cmp;
00414 
00415    switch (flags & OBJ_SEARCH_MASK) {
00416    case OBJ_SEARCH_OBJECT:
00417       right_key = get_caller_uniqueid(object_right);
00418       /* Fall through */
00419    case OBJ_SEARCH_KEY:
00420       cmp = strcmp(get_caller_uniqueid(object_left), right_key);
00421       break;
00422    case OBJ_SEARCH_PARTIAL_KEY:
00423       /*
00424        * We could also use a partial key struct containing a length
00425        * so strlen() does not get called for every comparison instead.
00426        */
00427       cmp = strncmp(get_caller_uniqueid(object_left), right_key, strlen(right_key));
00428       break;
00429    default:
00430       /*
00431        * What arg points to is specific to this traversal callback
00432        * and has no special meaning to astobj2.
00433        */
00434       cmp = 0;
00435       break;
00436    }
00437    if (cmp) {
00438       return 0;
00439    }
00440    /*
00441     * At this point the traversal callback is identical to a sorted
00442     * container.
00443     */
00444    return CMP_MATCH;
00445 }
00446 
00447 unsigned int ast_cel_check_enabled(void)
00448 {
00449    unsigned int enabled;
00450    struct cel_config *cfg = ao2_global_obj_ref(cel_configs);
00451 
00452    enabled = (!cfg || !cfg->general) ? 0 : cfg->general->enable;
00453    ao2_cleanup(cfg);
00454    return enabled;
00455 }
00456 
00457 static char *handle_cli_status(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00458 {
00459    unsigned int i;
00460    RAII_VAR(struct cel_config *, cfg, ao2_global_obj_ref(cel_configs), ao2_cleanup);
00461    RAII_VAR(struct ao2_container *, backends, ao2_global_obj_ref(cel_backends), ao2_cleanup);
00462    struct ao2_iterator iter;
00463    char *app;
00464 
00465    switch (cmd) {
00466    case CLI_INIT:
00467       e->command = "cel show status";
00468       e->usage =
00469          "Usage: cel show status\n"
00470          "       Displays the Channel Event Logging system status.\n";
00471       return NULL;
00472    case CLI_GENERATE:
00473       return NULL;
00474    case CLI_HANDLER:
00475       break;
00476    }
00477 
00478    if (a->argc > 3) {
00479       return CLI_SHOWUSAGE;
00480    }
00481 
00482    ast_cli(a->fd, "CEL Logging: %s\n", ast_cel_check_enabled() ? "Enabled" : "Disabled");
00483 
00484    if (!cfg || !cfg->general || !cfg->general->enable) {
00485       return CLI_SUCCESS;
00486    }
00487 
00488    for (i = 0; i < (sizeof(cfg->general->events) * 8); i++) {
00489       const char *name;
00490 
00491       if (!(cfg->general->events & ((int64_t) 1 << i))) {
00492          continue;
00493       }
00494 
00495       name = ast_cel_get_type_name(i);
00496       if (strcasecmp(name, "Unknown")) {
00497          ast_cli(a->fd, "CEL Tracking Event: %s\n", name);
00498       }
00499    }
00500 
00501    iter = ao2_iterator_init(cfg->general->apps, 0);
00502    for (; (app = ao2_iterator_next(&iter)); ao2_ref(app, -1)) {
00503       ast_cli(a->fd, "CEL Tracking Application: %s\n", app);
00504    }
00505    ao2_iterator_destroy(&iter);
00506 
00507    if (backends) {
00508       struct cel_backend *backend;
00509 
00510       iter = ao2_iterator_init(backends, 0);
00511       for (; (backend = ao2_iterator_next(&iter)); ao2_ref(backend, -1)) {
00512          ast_cli(a->fd, "CEL Event Subscriber: %s\n", backend->name);
00513       }
00514       ao2_iterator_destroy(&iter);
00515    }
00516 
00517    return CLI_SUCCESS;
00518 }
00519 
00520 static struct ast_cli_entry cli_status = AST_CLI_DEFINE(handle_cli_status, "Display the CEL status");
00521 
00522 enum ast_cel_event_type ast_cel_str_to_event_type(const char *name)
00523 {
00524    unsigned int i;
00525 
00526    for (i = 0; i < ARRAY_LEN(cel_event_types); i++) {
00527       if (cel_event_types[i] && !strcasecmp(name, cel_event_types[i])) {
00528          return i;
00529       }
00530    }
00531 
00532    ast_log(LOG_ERROR, "Unknown event name '%s'\n", name);
00533    return AST_CEL_INVALID_VALUE;
00534 }
00535 
00536 static int ast_cel_track_event(enum ast_cel_event_type et)
00537 {
00538    RAII_VAR(struct cel_config *, cfg, ao2_global_obj_ref(cel_configs), ao2_cleanup);
00539 
00540    if (!cfg || !cfg->general) {
00541       return 0;
00542    }
00543 
00544    return (cfg->general->events & ((int64_t) 1 << et));
00545 }
00546 
00547 static int events_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
00548 {
00549    struct ast_cel_general_config *cfg = obj;
00550    char *events = ast_strdupa(var->value);
00551    char *cur_event;
00552 
00553    while ((cur_event = strsep(&events, ","))) {
00554       enum ast_cel_event_type event_type;
00555 
00556       cur_event = ast_strip(cur_event);
00557       if (ast_strlen_zero(cur_event)) {
00558          continue;
00559       }
00560 
00561       event_type = ast_cel_str_to_event_type(cur_event);
00562 
00563       if (event_type == AST_CEL_ALL) {
00564          /* All events */
00565          cfg->events = (int64_t) -1;
00566       } else if (event_type == AST_CEL_INVALID_VALUE) {
00567          return -1;
00568       } else {
00569          cfg->events |= ((int64_t) 1 << event_type);
00570       }
00571    }
00572 
00573    return 0;
00574 }
00575 
00576 static int apps_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
00577 {
00578    struct ast_cel_general_config *cfg = obj;
00579    char *apps = ast_strdupa(var->value);
00580    char *cur_app;
00581 
00582    while ((cur_app = strsep(&apps, ","))) {
00583       cur_app = ast_strip(cur_app);
00584       if (ast_strlen_zero(cur_app)) {
00585          continue;
00586       }
00587 
00588       cur_app = ast_str_to_lower(cur_app);
00589       ast_str_container_add(cfg->apps, cur_app);
00590    }
00591 
00592    return 0;
00593 }
00594 
00595 const char *ast_cel_get_type_name(enum ast_cel_event_type type)
00596 {
00597    return S_OR(cel_event_types[type], "Unknown");
00598 }
00599 
00600 static int cel_track_app(const char *const_app)
00601 {
00602    RAII_VAR(struct cel_config *, cfg, ao2_global_obj_ref(cel_configs), ao2_cleanup);
00603    RAII_VAR(char *, app, NULL, ao2_cleanup);
00604    char *app_lower;
00605 
00606    if (!cfg || !cfg->general) {
00607       return 0;
00608    }
00609 
00610    app_lower = ast_str_to_lower(ast_strdupa(const_app));
00611    app = ao2_find(cfg->general->apps, app_lower, OBJ_SEARCH_KEY);
00612    if (!app) {
00613       return 0;
00614    }
00615 
00616    return 1;
00617 }
00618 
00619 static int cel_linkedid_ref(const char *linkedid);
00620 
00621 struct ast_event *ast_cel_create_event(struct ast_channel_snapshot *snapshot,
00622       enum ast_cel_event_type event_type, const char *userdefevname,
00623       struct ast_json *extra, const char *peer)
00624 {
00625    struct timeval eventtime = ast_tvnow();
00626    RAII_VAR(char *, extra_txt, NULL, ast_json_free);
00627    if (extra) {
00628       extra_txt = ast_json_dump_string(extra);
00629    }
00630    return ast_event_new(AST_EVENT_CEL,
00631       AST_EVENT_IE_CEL_EVENT_TYPE, AST_EVENT_IE_PLTYPE_UINT, event_type,
00632       AST_EVENT_IE_CEL_EVENT_TIME, AST_EVENT_IE_PLTYPE_UINT, eventtime.tv_sec,
00633       AST_EVENT_IE_CEL_EVENT_TIME_USEC, AST_EVENT_IE_PLTYPE_UINT, eventtime.tv_usec,
00634       AST_EVENT_IE_CEL_USEREVENT_NAME, AST_EVENT_IE_PLTYPE_STR, S_OR(userdefevname, ""),
00635       AST_EVENT_IE_CEL_CIDNAME, AST_EVENT_IE_PLTYPE_STR, snapshot->caller_name,
00636       AST_EVENT_IE_CEL_CIDNUM, AST_EVENT_IE_PLTYPE_STR, snapshot->caller_number,
00637       AST_EVENT_IE_CEL_CIDANI, AST_EVENT_IE_PLTYPE_STR, snapshot->caller_ani,
00638       AST_EVENT_IE_CEL_CIDRDNIS, AST_EVENT_IE_PLTYPE_STR, snapshot->caller_rdnis,
00639       AST_EVENT_IE_CEL_CIDDNID, AST_EVENT_IE_PLTYPE_STR, snapshot->caller_dnid,
00640       AST_EVENT_IE_CEL_EXTEN, AST_EVENT_IE_PLTYPE_STR, snapshot->exten,
00641       AST_EVENT_IE_CEL_CONTEXT, AST_EVENT_IE_PLTYPE_STR, snapshot->context,
00642       AST_EVENT_IE_CEL_CHANNAME, AST_EVENT_IE_PLTYPE_STR, snapshot->name,
00643       AST_EVENT_IE_CEL_APPNAME, AST_EVENT_IE_PLTYPE_STR, snapshot->appl,
00644       AST_EVENT_IE_CEL_APPDATA, AST_EVENT_IE_PLTYPE_STR, snapshot->data,
00645       AST_EVENT_IE_CEL_AMAFLAGS, AST_EVENT_IE_PLTYPE_UINT, snapshot->amaflags,
00646       AST_EVENT_IE_CEL_ACCTCODE, AST_EVENT_IE_PLTYPE_STR, snapshot->accountcode,
00647       AST_EVENT_IE_CEL_PEERACCT, AST_EVENT_IE_PLTYPE_STR, snapshot->peeraccount,
00648       AST_EVENT_IE_CEL_UNIQUEID, AST_EVENT_IE_PLTYPE_STR, snapshot->uniqueid,
00649       AST_EVENT_IE_CEL_LINKEDID, AST_EVENT_IE_PLTYPE_STR, snapshot->linkedid,
00650       AST_EVENT_IE_CEL_USERFIELD, AST_EVENT_IE_PLTYPE_STR, snapshot->userfield,
00651       AST_EVENT_IE_CEL_EXTRA, AST_EVENT_IE_PLTYPE_STR, S_OR(extra_txt, ""),
00652       AST_EVENT_IE_CEL_PEER, AST_EVENT_IE_PLTYPE_STR, S_OR(peer, ""),
00653       AST_EVENT_IE_END);
00654 }
00655 
00656 static int cel_backend_send_cb(void *obj, void *arg, int flags)
00657 {
00658    struct cel_backend *backend = obj;
00659 
00660    backend->callback(arg);
00661    return 0;
00662 }
00663 
00664 static int cel_report_event(struct ast_channel_snapshot *snapshot,
00665       enum ast_cel_event_type event_type, const char *userdefevname,
00666       struct ast_json *extra, const char *peer_str)
00667 {
00668    struct ast_event *ev;
00669    RAII_VAR(struct cel_config *, cfg, ao2_global_obj_ref(cel_configs), ao2_cleanup);
00670    RAII_VAR(struct ao2_container *, backends, ao2_global_obj_ref(cel_backends), ao2_cleanup);
00671 
00672    if (!cfg || !cfg->general || !cfg->general->enable || !backends) {
00673       return 0;
00674    }
00675 
00676    /* Record the linkedid of new channels if we are tracking LINKEDID_END even if we aren't
00677     * reporting on CHANNEL_START so we can track when to send LINKEDID_END */
00678    if (event_type == AST_CEL_CHANNEL_START
00679       && ast_cel_track_event(AST_CEL_LINKEDID_END)) {
00680       if (cel_linkedid_ref(snapshot->linkedid)) {
00681          return -1;
00682       }
00683    }
00684 
00685    if (!ast_cel_track_event(event_type)) {
00686       return 0;
00687    }
00688 
00689    if ((event_type == AST_CEL_APP_START || event_type == AST_CEL_APP_END)
00690       && !cel_track_app(snapshot->appl)) {
00691       return 0;
00692    }
00693 
00694    ev = ast_cel_create_event(snapshot, event_type, userdefevname, extra, peer_str);
00695    if (!ev) {
00696       return -1;
00697    }
00698 
00699    /* Distribute event to backends */
00700    ao2_callback(backends, OBJ_MULTIPLE | OBJ_NODATA, cel_backend_send_cb, ev);
00701    ast_event_destroy(ev);
00702 
00703    return 0;
00704 }
00705 
00706 /* called whenever a channel is destroyed or a linkedid is changed to
00707  * potentially emit a CEL_LINKEDID_END event */
00708 static void check_retire_linkedid(struct ast_channel_snapshot *snapshot)
00709 {
00710    RAII_VAR(struct ao2_container *, linkedids, ao2_global_obj_ref(cel_linkedids), ao2_cleanup);
00711    struct cel_linkedid *lid;
00712 
00713    if (!linkedids || ast_strlen_zero(snapshot->linkedid)) {
00714       /* The CEL module is shutdown.  Abort. */
00715       return;
00716    }
00717 
00718    ao2_lock(linkedids);
00719 
00720    lid = ao2_find(linkedids, (void *) snapshot->linkedid, OBJ_SEARCH_KEY);
00721    if (!lid) {
00722       ao2_unlock(linkedids);
00723 
00724       /*
00725        * The user may have done a reload to start tracking linkedids
00726        * when a call was already in progress.  This is an unusual kind
00727        * of change to make after starting Asterisk.
00728        */
00729       ast_log(LOG_ERROR, "Something weird happened, couldn't find linkedid %s\n",
00730          snapshot->linkedid);
00731       return;
00732    }
00733 
00734    if (!--lid->count) {
00735       /* No channels use this linkedid anymore. */
00736       ao2_unlink(linkedids, lid);
00737       ao2_unlock(linkedids);
00738 
00739       cel_report_event(snapshot, AST_CEL_LINKEDID_END, NULL, NULL, NULL);
00740    } else {
00741       ao2_unlock(linkedids);
00742    }
00743    ao2_ref(lid, -1);
00744 }
00745 
00746 /* Note that no 'chan_fixup' function is provided for this datastore type,
00747  * because the channels that will use it will never be involved in masquerades.
00748  */
00749 static const struct ast_datastore_info fabricated_channel_datastore = {
00750    .type = "CEL fabricated channel",
00751    .destroy = ast_free_ptr,
00752 };
00753 
00754 struct ast_channel *ast_cel_fabricate_channel_from_event(const struct ast_event *event)
00755 {
00756    struct varshead *headp;
00757    struct ast_var_t *newvariable;
00758    const char *mixed_name;
00759    char timebuf[30];
00760    struct ast_channel *tchan;
00761    struct ast_cel_event_record record = {
00762       .version = AST_CEL_EVENT_RECORD_VERSION,
00763    };
00764    struct ast_datastore *datastore;
00765    char *app_data;
00766    RAII_VAR(struct cel_config *, cfg, ao2_global_obj_ref(cel_configs), ao2_cleanup);
00767 
00768    if (!cfg || !cfg->general) {
00769       return NULL;
00770    }
00771 
00772    /* do not call ast_channel_alloc because this is not really a real channel */
00773    if (!(tchan = ast_dummy_channel_alloc())) {
00774       return NULL;
00775    }
00776 
00777    headp = ast_channel_varshead(tchan);
00778 
00779    /* first, get the variables from the event */
00780    if (ast_cel_fill_record(event, &record)) {
00781       ast_channel_unref(tchan);
00782       return NULL;
00783    }
00784 
00785    /* next, fill the channel with their data */
00786    mixed_name = (record.event_type == AST_CEL_USER_DEFINED)
00787       ? record.user_defined_name : record.event_name;
00788    if ((newvariable = ast_var_assign("eventtype", mixed_name))) {
00789       AST_LIST_INSERT_HEAD(headp, newvariable, entries);
00790    }
00791 
00792    if (ast_strlen_zero(cfg->general->date_format)) {
00793       snprintf(timebuf, sizeof(timebuf), "%ld.%06ld", (long) record.event_time.tv_sec,
00794             (long) record.event_time.tv_usec);
00795    } else {
00796       struct ast_tm tm;
00797       ast_localtime(&record.event_time, &tm, NULL);
00798       ast_strftime(timebuf, sizeof(timebuf), cfg->general->date_format, &tm);
00799    }
00800 
00801    if ((newvariable = ast_var_assign("eventtime", timebuf))) {
00802       AST_LIST_INSERT_HEAD(headp, newvariable, entries);
00803    }
00804 
00805    if ((newvariable = ast_var_assign("eventenum", record.event_name))) {
00806       AST_LIST_INSERT_HEAD(headp, newvariable, entries);
00807    }
00808    if ((newvariable = ast_var_assign("userdeftype", record.user_defined_name))) {
00809       AST_LIST_INSERT_HEAD(headp, newvariable, entries);
00810    }
00811    if ((newvariable = ast_var_assign("eventextra", record.extra))) {
00812       AST_LIST_INSERT_HEAD(headp, newvariable, entries);
00813    }
00814 
00815    ast_channel_caller(tchan)->id.name.valid = 1;
00816    ast_channel_caller(tchan)->id.name.str = ast_strdup(record.caller_id_name);
00817    ast_channel_caller(tchan)->id.number.valid = 1;
00818    ast_channel_caller(tchan)->id.number.str = ast_strdup(record.caller_id_num);
00819    ast_channel_caller(tchan)->ani.number.valid = 1;
00820    ast_channel_caller(tchan)->ani.number.str = ast_strdup(record.caller_id_ani);
00821    ast_channel_redirecting(tchan)->from.number.valid = 1;
00822    ast_channel_redirecting(tchan)->from.number.str = ast_strdup(record.caller_id_rdnis);
00823    ast_channel_dialed(tchan)->number.str = ast_strdup(record.caller_id_dnid);
00824 
00825    ast_channel_exten_set(tchan, record.extension);
00826    ast_channel_context_set(tchan, record.context);
00827    ast_channel_name_set(tchan, record.channel_name);
00828    ast_channel_internal_set_fake_ids(tchan, record.unique_id, record.linked_id);
00829    ast_channel_accountcode_set(tchan, record.account_code);
00830    ast_channel_peeraccount_set(tchan, record.peer_account);
00831    ast_channel_userfield_set(tchan, record.user_field);
00832 
00833    if ((newvariable = ast_var_assign("BRIDGEPEER", record.peer))) {
00834       AST_LIST_INSERT_HEAD(headp, newvariable, entries);
00835    }
00836 
00837    ast_channel_amaflags_set(tchan, record.amaflag);
00838 
00839    /* We need to store an 'application name' and 'application
00840     * data' on the channel for logging purposes, but the channel
00841     * structure only provides a place to store pointers, and it
00842     * expects these pointers to be pointing to data that does not
00843     * need to be freed. This means that the channel's destructor
00844     * does not attempt to free any storage that these pointers
00845     * point to. However, we can't provide data in that form directly for
00846     * these structure members. In order to ensure that these data
00847     * elements have a lifetime that matches the channel's
00848     * lifetime, we'll put them in a datastore attached to the
00849     * channel, and set's the channel's pointers to point into the
00850     * datastore.  The datastore will then be automatically destroyed
00851     * when the channel is destroyed.
00852     */
00853 
00854    if (!(datastore = ast_datastore_alloc(&fabricated_channel_datastore, NULL))) {
00855       ast_channel_unref(tchan);
00856       return NULL;
00857    }
00858 
00859    if (!(app_data = ast_malloc(strlen(record.application_name) + strlen(record.application_data) + 2))) {
00860       ast_datastore_free(datastore);
00861       ast_channel_unref(tchan);
00862       return NULL;
00863    }
00864 
00865    ast_channel_appl_set(tchan, strcpy(app_data, record.application_name));
00866    ast_channel_data_set(tchan, strcpy(app_data + strlen(record.application_name) + 1,
00867       record.application_data));
00868 
00869    datastore->data = app_data;
00870    ast_channel_datastore_add(tchan, datastore);
00871 
00872    return tchan;
00873 }
00874 
00875 static int cel_linkedid_ref(const char *linkedid)
00876 {
00877    RAII_VAR(struct ao2_container *, linkedids, ao2_global_obj_ref(cel_linkedids), ao2_cleanup);
00878    struct cel_linkedid *lid;
00879 
00880    if (ast_strlen_zero(linkedid)) {
00881       ast_log(LOG_ERROR, "The linkedid should never be empty\n");
00882       return -1;
00883    }
00884    if (!linkedids) {
00885       /* The CEL module is shutdown.  Abort. */
00886       return -1;
00887    }
00888 
00889    ao2_lock(linkedids);
00890    lid = ao2_find(linkedids, (void *) linkedid, OBJ_SEARCH_KEY);
00891    if (!lid) {
00892       /*
00893        * Changes to the lid->count member are protected by the
00894        * container lock so the lid object does not need its own lock.
00895        */
00896       lid = ao2_alloc_options(sizeof(*lid) + strlen(linkedid) + 1, NULL,
00897          AO2_ALLOC_OPT_LOCK_NOLOCK);
00898       if (!lid) {
00899          ao2_unlock(linkedids);
00900          return -1;
00901       }
00902       strcpy(lid->id, linkedid);/* Safe */
00903 
00904       ao2_link(linkedids, lid);
00905    }
00906    ++lid->count;
00907    ao2_unlock(linkedids);
00908    ao2_ref(lid, -1);
00909 
00910    return 0;
00911 }
00912 
00913 int ast_cel_fill_record(const struct ast_event *e, struct ast_cel_event_record *r)
00914 {
00915    if (r->version != AST_CEL_EVENT_RECORD_VERSION) {
00916       ast_log(LOG_ERROR, "Module ABI mismatch for ast_cel_event_record.  "
00917             "Please ensure all modules were compiled for "
00918             "this version of Asterisk.\n");
00919       return -1;
00920    }
00921 
00922    r->event_type = ast_event_get_ie_uint(e, AST_EVENT_IE_CEL_EVENT_TYPE);
00923 
00924    r->event_time.tv_sec = ast_event_get_ie_uint(e, AST_EVENT_IE_CEL_EVENT_TIME);
00925    r->event_time.tv_usec = ast_event_get_ie_uint(e, AST_EVENT_IE_CEL_EVENT_TIME_USEC);
00926 
00927    r->event_name = ast_cel_get_type_name(r->event_type);
00928    if (r->event_type == AST_CEL_USER_DEFINED) {
00929       r->user_defined_name = ast_event_get_ie_str(e, AST_EVENT_IE_CEL_USEREVENT_NAME);
00930    } else {
00931       r->user_defined_name = "";
00932    }
00933 
00934    r->caller_id_name   = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_CIDNAME), "");
00935    r->caller_id_num    = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_CIDNUM), "");
00936    r->caller_id_ani    = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_CIDANI), "");
00937    r->caller_id_rdnis  = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_CIDRDNIS), "");
00938    r->caller_id_dnid   = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_CIDDNID), "");
00939    r->extension        = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_EXTEN), "");
00940    r->context          = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_CONTEXT), "");
00941    r->channel_name     = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_CHANNAME), "");
00942    r->application_name = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_APPNAME), "");
00943    r->application_data = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_APPDATA), "");
00944    r->account_code     = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_ACCTCODE), "");
00945    r->peer_account     = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_PEERACCT), "");
00946    r->unique_id        = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_UNIQUEID), "");
00947    r->linked_id        = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_LINKEDID), "");
00948    r->amaflag          = ast_event_get_ie_uint(e, AST_EVENT_IE_CEL_AMAFLAGS);
00949    r->user_field       = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_USERFIELD), "");
00950    r->peer             = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_PEER), "");
00951    r->extra            = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_EXTRA), "");
00952 
00953    return 0;
00954 }
00955 
00956 /*! \brief Typedef for callbacks that get called on channel snapshot updates */
00957 typedef void (*cel_channel_snapshot_monitor)(
00958    struct ast_channel_snapshot *old_snapshot,
00959    struct ast_channel_snapshot *new_snapshot);
00960 
00961 static struct ast_multi_channel_blob *get_dialstatus_blob(const char *uniqueid)
00962 {
00963    struct ao2_container *dial_statuses = ao2_global_obj_ref(cel_dialstatus_store);
00964    struct ast_multi_channel_blob *blob = NULL;
00965 
00966    if (dial_statuses) {
00967       blob = ao2_find(dial_statuses, uniqueid, OBJ_SEARCH_KEY | OBJ_UNLINK);
00968       ao2_ref(dial_statuses, -1);
00969    }
00970    return blob;
00971 }
00972 
00973 static const char *get_blob_variable(struct ast_multi_channel_blob *blob, const char *varname)
00974 {
00975    struct ast_json *json = ast_multi_channel_blob_get_json(blob);
00976    if (!json) {
00977       return NULL;
00978    }
00979 
00980    json = ast_json_object_get(json, varname);
00981    if (!json) {
00982       return NULL;
00983    }
00984 
00985    return ast_json_string_get(json);
00986 }
00987 
00988 /*! \brief Handle channel state changes */
00989 static void cel_channel_state_change(
00990    struct ast_channel_snapshot *old_snapshot,
00991    struct ast_channel_snapshot *new_snapshot)
00992 {
00993    int is_hungup, was_hungup;
00994 
00995    if (!new_snapshot) {
00996       cel_report_event(old_snapshot, AST_CEL_CHANNEL_END, NULL, NULL, NULL);
00997       if (ast_cel_track_event(AST_CEL_LINKEDID_END)) {
00998          check_retire_linkedid(old_snapshot);
00999       }
01000       return;
01001    }
01002 
01003    if (!old_snapshot) {
01004       cel_report_event(new_snapshot, AST_CEL_CHANNEL_START, NULL, NULL, NULL);
01005       return;
01006    }
01007 
01008    was_hungup = ast_test_flag(&old_snapshot->flags, AST_FLAG_DEAD) ? 1 : 0;
01009    is_hungup = ast_test_flag(&new_snapshot->flags, AST_FLAG_DEAD) ? 1 : 0;
01010 
01011    if (!was_hungup && is_hungup) {
01012       struct ast_json *extra;
01013       struct ast_multi_channel_blob *blob = get_dialstatus_blob(new_snapshot->uniqueid);
01014       const char *dialstatus = "";
01015 
01016       if (blob && !ast_strlen_zero(get_blob_variable(blob, "dialstatus"))) {
01017          dialstatus = get_blob_variable(blob, "dialstatus");
01018       }
01019       extra = ast_json_pack("{s: i, s: s, s: s}",
01020          "hangupcause", new_snapshot->hangupcause,
01021          "hangupsource", new_snapshot->hangupsource,
01022          "dialstatus", dialstatus);
01023       cel_report_event(new_snapshot, AST_CEL_HANGUP, NULL, extra, NULL);
01024       ast_json_unref(extra);
01025       ao2_cleanup(blob);
01026       return;
01027    }
01028 
01029    if (old_snapshot->state != new_snapshot->state && new_snapshot->state == AST_STATE_UP) {
01030       cel_report_event(new_snapshot, AST_CEL_ANSWER, NULL, NULL, NULL);
01031       return;
01032    }
01033 }
01034 
01035 static void cel_channel_linkedid_change(
01036    struct ast_channel_snapshot *old_snapshot,
01037    struct ast_channel_snapshot *new_snapshot)
01038 {
01039    if (!old_snapshot || !new_snapshot) {
01040       return;
01041    }
01042 
01043    ast_assert(!ast_strlen_zero(new_snapshot->linkedid));
01044    ast_assert(!ast_strlen_zero(old_snapshot->linkedid));
01045 
01046    if (ast_cel_track_event(AST_CEL_LINKEDID_END)
01047       && strcmp(old_snapshot->linkedid, new_snapshot->linkedid)) {
01048       cel_linkedid_ref(new_snapshot->linkedid);
01049       check_retire_linkedid(old_snapshot);
01050    }
01051 }
01052 
01053 static void cel_channel_app_change(
01054    struct ast_channel_snapshot *old_snapshot,
01055    struct ast_channel_snapshot *new_snapshot)
01056 {
01057    if (new_snapshot && old_snapshot
01058       && !strcmp(old_snapshot->appl, new_snapshot->appl)) {
01059       return;
01060    }
01061 
01062    /* old snapshot has an application, end it */
01063    if (old_snapshot && !ast_strlen_zero(old_snapshot->appl)) {
01064       cel_report_event(old_snapshot, AST_CEL_APP_END, NULL, NULL, NULL);
01065    }
01066 
01067    /* new snapshot has an application, start it */
01068    if (new_snapshot && !ast_strlen_zero(new_snapshot->appl)) {
01069       cel_report_event(new_snapshot, AST_CEL_APP_START, NULL, NULL, NULL);
01070    }
01071 }
01072 
01073 /* \brief Handlers for channel snapshot changes.
01074  * \note Order of the handlers matters. Application changes must come before state
01075  * changes to ensure that hangup notifications occur after application changes.
01076  * Linkedid checking should always come last.
01077  */
01078 cel_channel_snapshot_monitor cel_channel_monitors[] = {
01079    cel_channel_app_change,
01080    cel_channel_state_change,
01081    cel_channel_linkedid_change,
01082 };
01083 
01084 static int cel_filter_channel_snapshot(struct ast_channel_snapshot *snapshot)
01085 {
01086    if (!snapshot) {
01087       return 0;
01088    }
01089    return snapshot->tech_properties & AST_CHAN_TP_INTERNAL;
01090 }
01091 
01092 static void cel_snapshot_update_cb(void *data, struct stasis_subscription *sub,
01093    struct stasis_message *message)
01094 {
01095    struct stasis_cache_update *update = stasis_message_data(message);
01096    if (ast_channel_snapshot_type() == update->type) {
01097       struct ast_channel_snapshot *old_snapshot;
01098       struct ast_channel_snapshot *new_snapshot;
01099       size_t i;
01100 
01101       old_snapshot = stasis_message_data(update->old_snapshot);
01102       new_snapshot = stasis_message_data(update->new_snapshot);
01103 
01104       if (cel_filter_channel_snapshot(old_snapshot) || cel_filter_channel_snapshot(new_snapshot)) {
01105          return;
01106       }
01107 
01108       for (i = 0; i < ARRAY_LEN(cel_channel_monitors); ++i) {
01109          cel_channel_monitors[i](old_snapshot, new_snapshot);
01110       }
01111    }
01112 }
01113 
01114 static struct ast_str *cel_generate_peer_str(
01115    struct ast_bridge_snapshot *bridge,
01116    struct ast_channel_snapshot *chan)
01117 {
01118    struct ast_str *peer_str = ast_str_create(32);
01119    struct ao2_iterator i;
01120    char *current_chan = NULL;
01121 
01122    if (!peer_str) {
01123       return NULL;
01124    }
01125 
01126    for (i = ao2_iterator_init(bridge->channels, 0);
01127       (current_chan = ao2_iterator_next(&i));
01128       ao2_cleanup(current_chan)) {
01129       struct ast_channel_snapshot *current_snapshot;
01130 
01131       /* Don't add the channel for which this message is being generated */
01132       if (!strcmp(current_chan, chan->uniqueid)) {
01133          continue;
01134       }
01135 
01136       current_snapshot = ast_channel_snapshot_get_latest(current_chan);
01137       if (!current_snapshot) {
01138          continue;
01139       }
01140 
01141       ast_str_append(&peer_str, 0, "%s,", current_snapshot->name);
01142       ao2_cleanup(current_snapshot);
01143    }
01144    ao2_iterator_destroy(&i);
01145 
01146    /* Rip off the trailing comma */
01147    ast_str_truncate(peer_str, -1);
01148 
01149    return peer_str;
01150 }
01151 
01152 static void cel_bridge_enter_cb(
01153    void *data, struct stasis_subscription *sub,
01154    struct stasis_message *message)
01155 {
01156    struct ast_bridge_blob *blob = stasis_message_data(message);
01157    struct ast_bridge_snapshot *snapshot = blob->bridge;
01158    struct ast_channel_snapshot *chan_snapshot = blob->channel;
01159    RAII_VAR(struct ast_json *, extra, NULL, ast_json_unref);
01160    RAII_VAR(struct ast_str *, peer_str, NULL, ast_free);
01161 
01162    if (cel_filter_channel_snapshot(chan_snapshot)) {
01163       return;
01164    }
01165 
01166    extra = ast_json_pack("{s: s, s: s}",
01167       "bridge_id", snapshot->uniqueid,
01168       "bridge_technology", snapshot->technology);
01169    if (!extra) {
01170       return;
01171    }
01172 
01173    peer_str = cel_generate_peer_str(snapshot, chan_snapshot);
01174    if (!peer_str) {
01175       return;
01176    }
01177 
01178    cel_report_event(chan_snapshot, AST_CEL_BRIDGE_ENTER, NULL, extra, ast_str_buffer(peer_str));
01179 }
01180 
01181 static void cel_bridge_leave_cb(
01182    void *data, struct stasis_subscription *sub,
01183    struct stasis_message *message)
01184 {
01185    struct ast_bridge_blob *blob = stasis_message_data(message);
01186    struct ast_bridge_snapshot *snapshot = blob->bridge;
01187    struct ast_channel_snapshot *chan_snapshot = blob->channel;
01188    RAII_VAR(struct ast_json *, extra, NULL, ast_json_unref);
01189    RAII_VAR(struct ast_str *, peer_str, NULL, ast_free);
01190 
01191    if (cel_filter_channel_snapshot(chan_snapshot)) {
01192       return;
01193    }
01194 
01195    extra = ast_json_pack("{s: s, s: s}",
01196       "bridge_id", snapshot->uniqueid,
01197       "bridge_technology", snapshot->technology);
01198    if (!extra) {
01199       return;
01200    }
01201 
01202    peer_str = cel_generate_peer_str(snapshot, chan_snapshot);
01203    if (!peer_str) {
01204       return;
01205    }
01206 
01207    cel_report_event(chan_snapshot, AST_CEL_BRIDGE_EXIT, NULL, extra, ast_str_buffer(peer_str));
01208 }
01209 
01210 static void cel_parking_cb(
01211    void *data, struct stasis_subscription *sub,
01212    struct stasis_message *message)
01213 {
01214    struct ast_parked_call_payload *parked_payload = stasis_message_data(message);
01215    RAII_VAR(struct ast_json *, extra, NULL, ast_json_unref);
01216    const char *reason = NULL;
01217 
01218    switch (parked_payload->event_type) {
01219    case PARKED_CALL:
01220       extra = ast_json_pack("{s: s, s: s}",
01221          "parker_dial_string", parked_payload->parker_dial_string,
01222          "parking_lot", parked_payload->parkinglot);
01223       if (extra) {
01224          cel_report_event(parked_payload->parkee, AST_CEL_PARK_START, NULL, extra, NULL);
01225       }
01226       return;
01227    case PARKED_CALL_TIMEOUT:
01228       reason = "ParkedCallTimeOut";
01229       break;
01230    case PARKED_CALL_GIVEUP:
01231       reason = "ParkedCallGiveUp";
01232       break;
01233    case PARKED_CALL_UNPARKED:
01234       reason = "ParkedCallUnparked";
01235       break;
01236    case PARKED_CALL_FAILED:
01237       reason = "ParkedCallFailed";
01238       break;
01239    case PARKED_CALL_SWAP:
01240       reason = "ParkedCallSwap";
01241       break;
01242    }
01243 
01244    if (parked_payload->retriever) {
01245       extra = ast_json_pack("{s: s, s: s}",
01246          "reason", reason,
01247          "retriever", parked_payload->retriever->name);
01248    } else {
01249       extra = ast_json_pack("{s: s}", "reason", reason);
01250    }
01251 
01252    if (extra) {
01253       cel_report_event(parked_payload->parkee, AST_CEL_PARK_END, NULL, extra, NULL);
01254    }
01255 }
01256 
01257 static void save_dialstatus(struct ast_multi_channel_blob *blob)
01258 {
01259    struct ao2_container *dial_statuses = ao2_global_obj_ref(cel_dialstatus_store);
01260 
01261    ast_assert(blob != NULL);
01262 
01263    if (dial_statuses) {
01264       ao2_link(dial_statuses, blob);
01265       ao2_ref(dial_statuses, -1);
01266    }
01267 }
01268 
01269 static int is_valid_dialstatus(struct ast_multi_channel_blob *blob)
01270 {
01271    const char *dialstatus = get_blob_variable(blob, "dialstatus");
01272    int res = 0;
01273 
01274    if (ast_strlen_zero(dialstatus)) {
01275       res = 0;
01276    } else if (!strcasecmp(dialstatus, "CHANUNAVAIL")) {
01277       res = 1;
01278    } else if (!strcasecmp(dialstatus, "CONGESTION")) {
01279       res = 1;
01280    } else if (!strcasecmp(dialstatus, "NOANSWER")) {
01281       res = 1;
01282    } else if (!strcasecmp(dialstatus, "BUSY")) {
01283       res = 1;
01284    } else if (!strcasecmp(dialstatus, "ANSWER")) {
01285       res = 1;
01286    } else if (!strcasecmp(dialstatus, "CANCEL")) {
01287       res = 1;
01288    } else if (!strcasecmp(dialstatus, "DONTCALL")) {
01289       res = 1;
01290    } else if (!strcasecmp(dialstatus, "TORTURE")) {
01291       res = 1;
01292    } else if (!strcasecmp(dialstatus, "INVALIDARGS")) {
01293       res = 1;
01294    }
01295    return res;
01296 }
01297 
01298 static void cel_dial_cb(void *data, struct stasis_subscription *sub,
01299    struct stasis_message *message)
01300 {
01301    struct ast_multi_channel_blob *blob = stasis_message_data(message);
01302 
01303    if (cel_filter_channel_snapshot(ast_multi_channel_blob_get_channel(blob, "caller"))) {
01304       return;
01305    }
01306 
01307    if (!get_caller_uniqueid(blob)) {
01308       return;
01309    }
01310 
01311    if (!ast_strlen_zero(get_blob_variable(blob, "forward"))) {
01312       struct ast_channel_snapshot *caller = ast_multi_channel_blob_get_channel(blob, "caller");
01313       struct ast_json *extra;
01314 
01315       if (!caller) {
01316          return;
01317       }
01318 
01319       extra = ast_json_pack("{s: s}", "forward", get_blob_variable(blob, "forward"));
01320       if (extra) {
01321          cel_report_event(caller, AST_CEL_FORWARD, NULL, extra, NULL);
01322          ast_json_unref(extra);
01323       }
01324    }
01325 
01326    if (is_valid_dialstatus(blob)) {
01327       save_dialstatus(blob);
01328    }
01329 }
01330 
01331 static void cel_generic_cb(
01332    void *data, struct stasis_subscription *sub,
01333    struct stasis_message *message)
01334 {
01335    struct ast_channel_blob *obj = stasis_message_data(message);
01336    int event_type = ast_json_integer_get(ast_json_object_get(obj->blob, "event_type"));
01337    struct ast_json *event_details = ast_json_object_get(obj->blob, "event_details");
01338 
01339    switch (event_type) {
01340    case AST_CEL_USER_DEFINED:
01341       {
01342          const char *event = ast_json_string_get(ast_json_object_get(event_details, "event"));
01343          struct ast_json *extra = ast_json_object_get(event_details, "extra");
01344          cel_report_event(obj->snapshot, event_type, event, extra, NULL);
01345          break;
01346       }
01347    default:
01348       ast_log(LOG_ERROR, "Unhandled %s event blob\n", ast_cel_get_type_name(event_type));
01349       break;
01350    }
01351 }
01352 
01353 static void cel_blind_transfer_cb(
01354    void *data, struct stasis_subscription *sub,
01355    struct stasis_message *message)
01356 {
01357    struct ast_blind_transfer_message *transfer_msg = stasis_message_data(message);
01358    struct ast_channel_snapshot *chan_snapshot = transfer_msg->transferer;
01359    struct ast_bridge_snapshot *bridge_snapshot = transfer_msg->bridge;
01360    struct ast_json *extra;
01361 
01362    if (transfer_msg->result != AST_BRIDGE_TRANSFER_SUCCESS) {
01363       return;
01364    }
01365 
01366    extra = ast_json_pack("{s: s, s: s, s: s, s: s, s: s}",
01367       "extension", transfer_msg->exten,
01368       "context", transfer_msg->context,
01369       "bridge_id", bridge_snapshot->uniqueid,
01370       "transferee_channel_name", transfer_msg->transferee ? transfer_msg->transferee->name : "N/A",
01371       "transferee_channel_uniqueid", transfer_msg->transferee ? transfer_msg->transferee->uniqueid  : "N/A");
01372    if (extra) {
01373       cel_report_event(chan_snapshot, AST_CEL_BLINDTRANSFER, NULL, extra, NULL);
01374       ast_json_unref(extra);
01375    }
01376 }
01377 
01378 static void cel_attended_transfer_cb(
01379    void *data, struct stasis_subscription *sub,
01380    struct stasis_message *message)
01381 {
01382    struct ast_attended_transfer_message *xfer = stasis_message_data(message);
01383    struct ast_json *extra = NULL;
01384    struct ast_bridge_snapshot *bridge1, *bridge2;
01385    struct ast_channel_snapshot *channel1, *channel2;
01386 
01387    /* Make sure bridge1 is always non-NULL */
01388    if (!xfer->to_transferee.bridge_snapshot) {
01389       bridge1 = xfer->to_transfer_target.bridge_snapshot;
01390       bridge2 = xfer->to_transferee.bridge_snapshot;
01391       channel1 = xfer->to_transfer_target.channel_snapshot;
01392       channel2 = xfer->to_transferee.channel_snapshot;
01393    } else {
01394       bridge1 = xfer->to_transferee.bridge_snapshot;
01395       bridge2 = xfer->to_transfer_target.bridge_snapshot;
01396       channel1 = xfer->to_transferee.channel_snapshot;
01397       channel2 = xfer->to_transfer_target.channel_snapshot;
01398    }
01399 
01400    switch (xfer->dest_type) {
01401    case AST_ATTENDED_TRANSFER_DEST_FAIL:
01402       return;
01403       /* handle these three the same */
01404    case AST_ATTENDED_TRANSFER_DEST_BRIDGE_MERGE:
01405    case AST_ATTENDED_TRANSFER_DEST_LINK:
01406    case AST_ATTENDED_TRANSFER_DEST_THREEWAY:
01407       extra = ast_json_pack("{s: s, s: s, s: s, s: s, s: s, s: s, s: s, s: s}",
01408          "bridge1_id", bridge1->uniqueid,
01409          "channel2_name", channel2->name,
01410          "channel2_uniqueid", channel2->uniqueid,
01411          "bridge2_id", bridge2->uniqueid,
01412          "transferee_channel_name", xfer->transferee ? xfer->transferee->name : "N/A",
01413          "transferee_channel_uniqueid", xfer->transferee ? xfer->transferee->uniqueid : "N/A",
01414          "transfer_target_channel_name", xfer->target ? xfer->target->name : "N/A",
01415          "transfer_target_channel_uniqueid", xfer->target ? xfer->target->uniqueid : "N/A");
01416       if (!extra) {
01417          return;
01418       }
01419       break;
01420    case AST_ATTENDED_TRANSFER_DEST_APP:
01421    case AST_ATTENDED_TRANSFER_DEST_LOCAL_APP:
01422       extra = ast_json_pack("{s: s, s: s, s: s, s: s, s: s, s: s, s: s, s: s}",
01423          "bridge1_id", bridge1->uniqueid,
01424          "channel2_name", channel2->name,
01425          "channel2_uniqueid", channel2->uniqueid,
01426          "app", xfer->dest.app,
01427          "transferee_channel_name", xfer->transferee ? xfer->transferee->name : "N/A",
01428          "transferee_channel_uniqueid", xfer->transferee ? xfer->transferee->uniqueid : "N/A",
01429          "transfer_target_channel_name", xfer->target ? xfer->target->name : "N/A",
01430          "transfer_target_channel_uniqueid", xfer->target ? xfer->target->uniqueid : "N/A");
01431       if (!extra) {
01432          return;
01433       }
01434       break;
01435    }
01436    cel_report_event(channel1, AST_CEL_ATTENDEDTRANSFER, NULL, extra, NULL);
01437    ast_json_unref(extra);
01438 }
01439 
01440 static void cel_pickup_cb(
01441    void *data, struct stasis_subscription *sub,
01442    struct stasis_message *message)
01443 {
01444    struct ast_multi_channel_blob *obj = stasis_message_data(message);
01445    struct ast_channel_snapshot *channel = ast_multi_channel_blob_get_channel(obj, "channel");
01446    struct ast_channel_snapshot *target = ast_multi_channel_blob_get_channel(obj, "target");
01447    struct ast_json *extra;
01448 
01449    if (!channel || !target) {
01450       return;
01451    }
01452 
01453    extra = ast_json_pack("{s: s, s: s}",
01454       "pickup_channel", channel->name,
01455       "pickup_channel_uniqueid", channel->uniqueid);
01456    if (!extra) {
01457       return;
01458    }
01459 
01460    cel_report_event(target, AST_CEL_PICKUP, NULL, extra, NULL);
01461    ast_json_unref(extra);
01462 }
01463 
01464 static void cel_local_cb(
01465    void *data, struct stasis_subscription *sub,
01466    struct stasis_message *message)
01467 {
01468    struct ast_multi_channel_blob *obj = stasis_message_data(message);
01469    struct ast_channel_snapshot *localone = ast_multi_channel_blob_get_channel(obj, "1");
01470    struct ast_channel_snapshot *localtwo = ast_multi_channel_blob_get_channel(obj, "2");
01471    struct ast_json *extra;
01472 
01473    if (!localone || !localtwo) {
01474       return;
01475    }
01476 
01477    extra = ast_json_pack("{s: s, s: s}",
01478       "local_two", localtwo->name,
01479       "local_two_uniqueid", localtwo->uniqueid);
01480    if (!extra) {
01481       return;
01482    }
01483 
01484    cel_report_event(localone, AST_CEL_LOCAL_OPTIMIZE, NULL, extra, NULL);
01485    ast_json_unref(extra);
01486 }
01487 
01488 static void destroy_routes(void)
01489 {
01490    stasis_message_router_unsubscribe_and_join(cel_state_router);
01491    cel_state_router = NULL;
01492 }
01493 
01494 static void destroy_subscriptions(void)
01495 {
01496    ao2_cleanup(cel_aggregation_topic);
01497    cel_aggregation_topic = NULL;
01498    ao2_cleanup(cel_topic);
01499    cel_topic = NULL;
01500 
01501    cel_channel_forwarder = stasis_forward_cancel(cel_channel_forwarder);
01502    cel_bridge_forwarder = stasis_forward_cancel(cel_bridge_forwarder);
01503    cel_parking_forwarder = stasis_forward_cancel(cel_parking_forwarder);
01504    cel_cel_forwarder = stasis_forward_cancel(cel_cel_forwarder);
01505 }
01506 
01507 static void cel_engine_cleanup(void)
01508 {
01509    destroy_routes();
01510    destroy_subscriptions();
01511    STASIS_MESSAGE_TYPE_CLEANUP(cel_generic_type);
01512 
01513    ast_cli_unregister(&cli_status);
01514    aco_info_destroy(&cel_cfg_info);
01515    ao2_global_obj_release(cel_configs);
01516    ao2_global_obj_release(cel_dialstatus_store);
01517    ao2_global_obj_release(cel_linkedids);
01518    ao2_global_obj_release(cel_backends);
01519 }
01520 
01521 /*!
01522  * \brief Create the Stasis subscriptions for CEL
01523  */
01524 static int create_subscriptions(void)
01525 {
01526    cel_aggregation_topic = stasis_topic_create("cel_aggregation_topic");
01527    if (!cel_aggregation_topic) {
01528       return -1;
01529    }
01530 
01531    cel_topic = stasis_topic_create("cel_topic");
01532    if (!cel_topic) {
01533       return -1;
01534    }
01535 
01536    cel_channel_forwarder = stasis_forward_all(
01537       ast_channel_topic_all_cached(),
01538       cel_aggregation_topic);
01539    if (!cel_channel_forwarder) {
01540       return -1;
01541    }
01542 
01543    cel_bridge_forwarder = stasis_forward_all(
01544       ast_bridge_topic_all_cached(),
01545       cel_aggregation_topic);
01546    if (!cel_bridge_forwarder) {
01547       return -1;
01548    }
01549 
01550    cel_parking_forwarder = stasis_forward_all(
01551       ast_parking_topic(),
01552       cel_aggregation_topic);
01553    if (!cel_parking_forwarder) {
01554       return -1;
01555    }
01556 
01557    cel_cel_forwarder = stasis_forward_all(
01558       ast_cel_topic(),
01559       cel_aggregation_topic);
01560    if (!cel_cel_forwarder) {
01561       return -1;
01562    }
01563 
01564    return 0;
01565 }
01566 
01567 /*!
01568  * \brief Create the Stasis message router and routes for CEL
01569  */
01570 static int create_routes(void)
01571 {
01572    int ret = 0;
01573 
01574    cel_state_router = stasis_message_router_create(cel_aggregation_topic);
01575    if (!cel_state_router) {
01576       return -1;
01577    }
01578 
01579    ret |= stasis_message_router_add(cel_state_router,
01580       stasis_cache_update_type(),
01581       cel_snapshot_update_cb,
01582       NULL);
01583 
01584    ret |= stasis_message_router_add(cel_state_router,
01585       ast_channel_dial_type(),
01586       cel_dial_cb,
01587       NULL);
01588 
01589    ret |= stasis_message_router_add(cel_state_router,
01590       ast_channel_entered_bridge_type(),
01591       cel_bridge_enter_cb,
01592       NULL);
01593 
01594    ret |= stasis_message_router_add(cel_state_router,
01595       ast_channel_left_bridge_type(),
01596       cel_bridge_leave_cb,
01597       NULL);
01598 
01599    ret |= stasis_message_router_add(cel_state_router,
01600       ast_parked_call_type(),
01601       cel_parking_cb,
01602       NULL);
01603 
01604    ret |= stasis_message_router_add(cel_state_router,
01605       cel_generic_type(),
01606       cel_generic_cb,
01607       NULL);
01608 
01609    ret |= stasis_message_router_add(cel_state_router,
01610       ast_blind_transfer_type(),
01611       cel_blind_transfer_cb,
01612       NULL);
01613 
01614    ret |= stasis_message_router_add(cel_state_router,
01615       ast_attended_transfer_type(),
01616       cel_attended_transfer_cb,
01617       NULL);
01618 
01619    ret |= stasis_message_router_add(cel_state_router,
01620       ast_call_pickup_type(),
01621       cel_pickup_cb,
01622       NULL);
01623 
01624    ret |= stasis_message_router_add(cel_state_router,
01625       ast_local_optimization_end_type(),
01626       cel_local_cb,
01627       NULL);
01628 
01629    if (ret) {
01630       ast_log(AST_LOG_ERROR, "Failed to register for Stasis messages\n");
01631    }
01632 
01633    return ret;
01634 }
01635 
01636 static int lid_hash(const void *obj, const int flags)
01637 {
01638    const struct cel_linkedid *lid;
01639    const char *key;
01640 
01641    switch (flags & OBJ_SEARCH_MASK) {
01642    case OBJ_SEARCH_KEY:
01643       key = obj;
01644       break;
01645    case OBJ_SEARCH_OBJECT:
01646       lid = obj;
01647       key = lid->id;
01648       break;
01649    default:
01650       /* Hash can only work on something with a full key. */
01651       ast_assert(0);
01652       return 0;
01653    }
01654    return ast_str_hash(key);
01655 }
01656 
01657 static int lid_cmp(void *obj, void *arg, int flags)
01658 {
01659    const struct cel_linkedid *object_left = obj;
01660    const struct cel_linkedid *object_right = arg;
01661    const char *right_key = arg;
01662    int cmp;
01663 
01664    switch (flags & OBJ_SEARCH_MASK) {
01665    case OBJ_SEARCH_OBJECT:
01666       right_key = object_right->id;
01667       /* Fall through */
01668    case OBJ_SEARCH_KEY:
01669       cmp = strcmp(object_left->id, right_key);
01670       break;
01671    case OBJ_SEARCH_PARTIAL_KEY:
01672       /*
01673        * We could also use a partial key struct containing a length
01674        * so strlen() does not get called for every comparison instead.
01675        */
01676       cmp = strncmp(object_left->id, right_key, strlen(right_key));
01677       break;
01678    default:
01679       /*
01680        * What arg points to is specific to this traversal callback
01681        * and has no special meaning to astobj2.
01682        */
01683       cmp = 0;
01684       break;
01685    }
01686    if (cmp) {
01687       return 0;
01688    }
01689    /*
01690     * At this point the traversal callback is identical to a sorted
01691     * container.
01692     */
01693    return CMP_MATCH;
01694 }
01695 
01696 int ast_cel_engine_init(void)
01697 {
01698    struct ao2_container *container;
01699 
01700    container = ao2_container_alloc(NUM_APP_BUCKETS, lid_hash, lid_cmp);
01701    ao2_global_obj_replace_unref(cel_linkedids, container);
01702    ao2_cleanup(container);
01703    if (!container) {
01704       cel_engine_cleanup();
01705       return -1;
01706    }
01707 
01708    container = ao2_container_alloc(NUM_DIALSTATUS_BUCKETS,
01709       dialstatus_hash, dialstatus_cmp);
01710    ao2_global_obj_replace_unref(cel_dialstatus_store, container);
01711    ao2_cleanup(container);
01712    if (!container) {
01713       cel_engine_cleanup();
01714       return -1;
01715    }
01716 
01717    if (STASIS_MESSAGE_TYPE_INIT(cel_generic_type)) {
01718       cel_engine_cleanup();
01719       return -1;
01720    }
01721 
01722    if (ast_cli_register(&cli_status)) {
01723       cel_engine_cleanup();
01724       return -1;
01725    }
01726 
01727    container = ao2_container_alloc(BACKEND_BUCKETS, cel_backend_hash, cel_backend_cmp);
01728    ao2_global_obj_replace_unref(cel_backends, container);
01729    ao2_cleanup(container);
01730    if (!container) {
01731       cel_engine_cleanup();
01732       return -1;
01733    }
01734 
01735    if (aco_info_init(&cel_cfg_info)) {
01736       cel_engine_cleanup();
01737       return -1;
01738    }
01739 
01740    aco_option_register(&cel_cfg_info, "enable", ACO_EXACT, general_options, "no", OPT_BOOL_T, 1, FLDSET(struct ast_cel_general_config, enable));
01741    aco_option_register(&cel_cfg_info, "dateformat", ACO_EXACT, general_options, "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_cel_general_config, date_format));
01742    aco_option_register_custom(&cel_cfg_info, "apps", ACO_EXACT, general_options, "", apps_handler, 0);
01743    aco_option_register_custom(&cel_cfg_info, "events", ACO_EXACT, general_options, "", events_handler, 0);
01744 
01745    if (aco_process_config(&cel_cfg_info, 0)) {
01746       struct cel_config *cel_cfg = cel_config_alloc();
01747 
01748       if (!cel_cfg) {
01749          cel_engine_cleanup();
01750          return -1;
01751       }
01752 
01753       /* We couldn't process the configuration so create a default config. */
01754       if (!aco_set_defaults(&general_option, "general", cel_cfg->general)) {
01755          ast_log(LOG_NOTICE, "Failed to process CEL configuration; using defaults\n");
01756          ao2_global_obj_replace_unref(cel_configs, cel_cfg);
01757       }
01758       ao2_ref(cel_cfg, -1);
01759    }
01760 
01761    if (create_subscriptions()) {
01762       cel_engine_cleanup();
01763       return -1;
01764    }
01765 
01766    if (ast_cel_check_enabled() && create_routes()) {
01767       cel_engine_cleanup();
01768       return -1;
01769    }
01770 
01771    ast_register_cleanup(cel_engine_cleanup);
01772    return 0;
01773 }
01774 
01775 int ast_cel_engine_reload(void)
01776 {
01777    unsigned int was_enabled = ast_cel_check_enabled();
01778    unsigned int is_enabled;
01779 
01780    if (aco_process_config(&cel_cfg_info, 1) == ACO_PROCESS_ERROR) {
01781       return -1;
01782    }
01783 
01784    is_enabled = ast_cel_check_enabled();
01785 
01786    if (!was_enabled && is_enabled) {
01787       if (create_routes()) {
01788          return -1;
01789       }
01790    } else if (was_enabled && !is_enabled) {
01791       destroy_routes();
01792    }
01793 
01794    ast_verb(3, "CEL logging %sabled.\n", is_enabled ? "en" : "dis");
01795 
01796    return 0;
01797 }
01798 
01799 void ast_cel_publish_event(struct ast_channel *chan,
01800    enum ast_cel_event_type event_type,
01801    struct ast_json *blob)
01802 {
01803    struct ast_json *cel_blob;
01804    struct stasis_message *message;
01805 
01806    cel_blob = ast_json_pack("{s: i, s: O}",
01807       "event_type", event_type,
01808       "event_details", blob);
01809 
01810    message = ast_channel_blob_create_from_cache(ast_channel_uniqueid(chan), cel_generic_type(), cel_blob);
01811    if (message) {
01812       stasis_publish(ast_cel_topic(), message);
01813    }
01814    ao2_cleanup(message);
01815    ast_json_unref(cel_blob);
01816 }
01817 
01818 struct stasis_topic *ast_cel_topic(void)
01819 {
01820    return cel_topic;
01821 }
01822 
01823 struct ast_cel_general_config *ast_cel_get_config(void)
01824 {
01825    RAII_VAR(struct cel_config *, mod_cfg, ao2_global_obj_ref(cel_configs), ao2_cleanup);
01826 
01827    if (!mod_cfg || !mod_cfg->general) {
01828       return NULL;
01829    }
01830 
01831    ao2_ref(mod_cfg->general, +1);
01832    return mod_cfg->general;
01833 }
01834 
01835 void ast_cel_set_config(struct ast_cel_general_config *config)
01836 {
01837    int was_enabled;
01838    int is_enabled;
01839    struct ast_cel_general_config *cleanup_config;
01840    struct cel_config *mod_cfg = ao2_global_obj_ref(cel_configs);
01841 
01842    if (mod_cfg) {
01843       was_enabled = ast_cel_check_enabled();
01844 
01845       cleanup_config = mod_cfg->general;
01846       ao2_bump(config);
01847       mod_cfg->general = config;
01848       ao2_cleanup(cleanup_config);
01849 
01850       is_enabled = ast_cel_check_enabled();
01851       if (!was_enabled && is_enabled) {
01852          create_routes();
01853       } else if (was_enabled && !is_enabled) {
01854          destroy_routes();
01855       }
01856 
01857       ao2_ref(mod_cfg, -1);
01858    }
01859 }
01860 
01861 int ast_cel_backend_unregister(const char *name)
01862 {
01863    struct ao2_container *backends = ao2_global_obj_ref(cel_backends);
01864 
01865    if (backends) {
01866       ao2_find(backends, name, OBJ_SEARCH_KEY | OBJ_NODATA | OBJ_UNLINK);
01867       ao2_ref(backends, -1);
01868    }
01869 
01870    return 0;
01871 }
01872 
01873 int ast_cel_backend_register(const char *name, ast_cel_backend_cb backend_callback)
01874 {
01875    RAII_VAR(struct ao2_container *, backends, ao2_global_obj_ref(cel_backends), ao2_cleanup);
01876    struct cel_backend *backend;
01877 
01878    if (!backends || ast_strlen_zero(name) || !backend_callback) {
01879       return -1;
01880    }
01881 
01882    /* The backend object is immutable so it doesn't need a lock of its own. */
01883    backend = ao2_alloc_options(sizeof(*backend) + 1 + strlen(name), NULL,
01884       AO2_ALLOC_OPT_LOCK_NOLOCK);
01885    if (!backend) {
01886       return -1;
01887    }
01888    strcpy(backend->name, name);/* Safe */
01889    backend->callback = backend_callback;
01890 
01891    ao2_link(backends, backend);
01892    ao2_ref(backend, -1);
01893    return 0;
01894 }

Generated on Thu Apr 16 06:27:18 2015 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6