Newer
Older
struct ast_channel_snapshot *old_snapshot,
struct ast_channel_snapshot *new_snapshot)
{
int is_hungup, was_hungup;
cel_report_event(old_snapshot, AST_CEL_CHANNEL_END, NULL, NULL, NULL);
if (ast_cel_track_event(AST_CEL_LINKEDID_END)) {
check_retire_linkedid(old_snapshot);
}
return;
}
if (!old_snapshot) {
cel_report_event(new_snapshot, AST_CEL_CHANNEL_START, NULL, NULL, NULL);
Matthew Jordan
committed
was_hungup = ast_test_flag(&old_snapshot->flags, AST_FLAG_DEAD) ? 1 : 0;
is_hungup = ast_test_flag(&new_snapshot->flags, AST_FLAG_DEAD) ? 1 : 0;
if (!was_hungup && is_hungup) {
struct ast_json *extra;
struct cel_dialstatus *dialstatus = get_dialstatus(new_snapshot->uniqueid);
extra = ast_json_pack("{s: i, s: s, s: s}",
"hangupcause", new_snapshot->hangupcause,
"hangupsource", new_snapshot->hangupsource,
"dialstatus", dialstatus ? dialstatus->dialstatus : "");
cel_report_event(new_snapshot, AST_CEL_HANGUP, NULL, extra, NULL);
ast_json_unref(extra);
ao2_cleanup(dialstatus);
return;
}
if (old_snapshot->state != new_snapshot->state && new_snapshot->state == AST_STATE_UP) {
cel_report_event(new_snapshot, AST_CEL_ANSWER, NULL, NULL, NULL);
return;
}
}
static void cel_channel_linkedid_change(
struct ast_channel_snapshot *old_snapshot,
struct ast_channel_snapshot *new_snapshot)
{
if (!old_snapshot || !new_snapshot) {
return;
}
ast_assert(!ast_strlen_zero(new_snapshot->linkedid));
ast_assert(!ast_strlen_zero(old_snapshot->linkedid));
if (ast_cel_track_event(AST_CEL_LINKEDID_END)
&& strcmp(old_snapshot->linkedid, new_snapshot->linkedid)) {
cel_linkedid_ref(new_snapshot->linkedid);
check_retire_linkedid(old_snapshot);
}
}
static void cel_channel_app_change(
struct ast_channel_snapshot *old_snapshot,
struct ast_channel_snapshot *new_snapshot)
{
if (new_snapshot && old_snapshot
&& !strcmp(old_snapshot->appl, new_snapshot->appl)) {
return;
}
/* old snapshot has an application, end it */
if (old_snapshot && !ast_strlen_zero(old_snapshot->appl)) {
cel_report_event(old_snapshot, AST_CEL_APP_END, NULL, NULL, NULL);
}
/* new snapshot has an application, start it */
if (new_snapshot && !ast_strlen_zero(new_snapshot->appl)) {
cel_report_event(new_snapshot, AST_CEL_APP_START, NULL, NULL, NULL);
/* \brief Handlers for channel snapshot changes.
* \note Order of the handlers matters. Application changes must come before state
* changes to ensure that hangup notifications occur after application changes.
* Linkedid checking should always come last.
*/
cel_channel_snapshot_monitor cel_channel_monitors[] = {
cel_channel_app_change,
cel_channel_state_change,
cel_channel_linkedid_change,
};
static int cel_filter_channel_snapshot(struct ast_channel_snapshot *snapshot)
{
if (!snapshot) {
return 0;
}
Matthew Jordan
committed
return snapshot->tech_properties & AST_CHAN_TP_INTERNAL;
static void cel_snapshot_update_cb(void *data, struct stasis_subscription *sub,
struct stasis_message *message)
{
struct stasis_cache_update *update = stasis_message_data(message);
if (ast_channel_snapshot_type() == update->type) {
struct ast_channel_snapshot *old_snapshot;
struct ast_channel_snapshot *new_snapshot;
size_t i;
old_snapshot = stasis_message_data(update->old_snapshot);
new_snapshot = stasis_message_data(update->new_snapshot);
if (cel_filter_channel_snapshot(old_snapshot) || cel_filter_channel_snapshot(new_snapshot)) {
return;
}
for (i = 0; i < ARRAY_LEN(cel_channel_monitors); ++i) {
cel_channel_monitors[i](old_snapshot, new_snapshot);
}
}
}
static struct ast_str *cel_generate_peer_str(
struct ast_bridge_snapshot *bridge,
struct ast_channel_snapshot *chan)
{
struct ast_str *peer_str = ast_str_create(32);
struct ao2_iterator i;
char *current_chan = NULL;
if (!peer_str) {
return NULL;
}
for (i = ao2_iterator_init(bridge->channels, 0);
(current_chan = ao2_iterator_next(&i));
ao2_cleanup(current_chan)) {
struct ast_channel_snapshot *current_snapshot;
/* Don't add the channel for which this message is being generated */
if (!strcmp(current_chan, chan->uniqueid)) {
continue;
}
current_snapshot = ast_channel_snapshot_get_latest(current_chan);
if (!current_snapshot) {
continue;
}
ast_str_append(&peer_str, 0, "%s,", current_snapshot->name);
ao2_cleanup(current_snapshot);
}
ao2_iterator_destroy(&i);
/* Rip off the trailing comma */
ast_str_truncate(peer_str, -1);
return peer_str;
}
static void cel_bridge_enter_cb(
void *data, struct stasis_subscription *sub,
struct stasis_message *message)
{
struct ast_bridge_blob *blob = stasis_message_data(message);
struct ast_bridge_snapshot *snapshot = blob->bridge;
struct ast_channel_snapshot *chan_snapshot = blob->channel;
RAII_VAR(struct ast_json *, extra, NULL, ast_json_unref);
RAII_VAR(struct ast_str *, peer_str, NULL, ast_free);
if (cel_filter_channel_snapshot(chan_snapshot)) {
return;
}
extra = ast_json_pack("{s: s, s: s}",
"bridge_id", snapshot->uniqueid,
"bridge_technology", snapshot->technology);
if (!extra) {
return;
peer_str = cel_generate_peer_str(snapshot, chan_snapshot);
if (!peer_str) {
return;
}
cel_report_event(chan_snapshot, AST_CEL_BRIDGE_ENTER, NULL, extra, ast_str_buffer(peer_str));
}
static void cel_bridge_leave_cb(
void *data, struct stasis_subscription *sub,
struct stasis_message *message)
{
struct ast_bridge_blob *blob = stasis_message_data(message);
struct ast_bridge_snapshot *snapshot = blob->bridge;
struct ast_channel_snapshot *chan_snapshot = blob->channel;
RAII_VAR(struct ast_json *, extra, NULL, ast_json_unref);
RAII_VAR(struct ast_str *, peer_str, NULL, ast_free);
if (cel_filter_channel_snapshot(chan_snapshot)) {
return;
}
extra = ast_json_pack("{s: s, s: s}",
"bridge_id", snapshot->uniqueid,
"bridge_technology", snapshot->technology);
if (!extra) {
return;
peer_str = cel_generate_peer_str(snapshot, chan_snapshot);
if (!peer_str) {
return;
}
cel_report_event(chan_snapshot, AST_CEL_BRIDGE_EXIT, NULL, extra, ast_str_buffer(peer_str));
}
static void cel_parking_cb(
void *data, struct stasis_subscription *sub,
struct stasis_message *message)
{
struct ast_parked_call_payload *parked_payload = stasis_message_data(message);
RAII_VAR(struct ast_json *, extra, NULL, ast_json_unref);
const char *reason = NULL;
switch (parked_payload->event_type) {
case PARKED_CALL:
extra = ast_json_pack("{s: s, s: s}",
"parker_dial_string", parked_payload->parker_dial_string,
"parking_lot", parked_payload->parkinglot);
if (extra) {
cel_report_event(parked_payload->parkee, AST_CEL_PARK_START, NULL, extra, NULL);
case PARKED_CALL_TIMEOUT:
break;
case PARKED_CALL_GIVEUP:
break;
case PARKED_CALL_UNPARKED:
break;
case PARKED_CALL_FAILED:
Mark Michelson
committed
case PARKED_CALL_SWAP:
reason = "ParkedCallSwap";
break;
if (parked_payload->retriever) {
extra = ast_json_pack("{s: s, s: s}",
"reason", reason ?: "",
"retriever", parked_payload->retriever->name);
} else {
extra = ast_json_pack("{s: s}", "reason", reason ?: "");
cel_report_event(parked_payload->parkee, AST_CEL_PARK_END, NULL, extra, NULL);
static void save_dialstatus(struct ast_multi_channel_blob *blob, struct ast_channel_snapshot *snapshot)
struct ao2_container *dial_statuses = ao2_global_obj_ref(cel_dialstatus_store);
const char *dialstatus_string = get_blob_variable(blob, "dialstatus");
struct cel_dialstatus *dialstatus;
size_t dialstatus_string_len;
if (!dial_statuses || ast_strlen_zero(dialstatus_string)) {
ao2_cleanup(dial_statuses);
return;
}
dialstatus = ao2_find(dial_statuses, snapshot->uniqueid, OBJ_SEARCH_KEY);
if (dialstatus) {
if (!strcasecmp(dialstatus_string, "ANSWER") && strcasecmp(dialstatus->dialstatus, "ANSWER")) {
/* In the case of an answer after we already have a dial status we give
* priority to the answer since the call was, well, answered. In the case of
* failure dial status results we simply let the first failure be the status.
*/
ao2_unlink(dial_statuses, dialstatus);
ao2_ref(dialstatus, -1);
} else {
ao2_ref(dialstatus, -1);
ao2_ref(dial_statuses, -1);
return;
}
}
dialstatus_string_len = strlen(dialstatus_string) + 1;
dialstatus = ao2_alloc_options(sizeof(*dialstatus) + dialstatus_string_len, NULL,
AO2_ALLOC_OPT_LOCK_NOLOCK);
if (!dialstatus) {
ao2_ref(dial_statuses, -1);
ast_copy_string(dialstatus->uniqueid, snapshot->uniqueid, sizeof(dialstatus->uniqueid));
ast_copy_string(dialstatus->dialstatus, dialstatus_string, dialstatus_string_len);
ao2_link(dial_statuses, dialstatus);
ao2_ref(dialstatus, -1);
ao2_ref(dial_statuses, -1);
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
static int is_valid_dialstatus(struct ast_multi_channel_blob *blob)
{
const char *dialstatus = get_blob_variable(blob, "dialstatus");
int res = 0;
if (ast_strlen_zero(dialstatus)) {
res = 0;
} else if (!strcasecmp(dialstatus, "CHANUNAVAIL")) {
res = 1;
} else if (!strcasecmp(dialstatus, "CONGESTION")) {
res = 1;
} else if (!strcasecmp(dialstatus, "NOANSWER")) {
res = 1;
} else if (!strcasecmp(dialstatus, "BUSY")) {
res = 1;
} else if (!strcasecmp(dialstatus, "ANSWER")) {
res = 1;
} else if (!strcasecmp(dialstatus, "CANCEL")) {
res = 1;
} else if (!strcasecmp(dialstatus, "DONTCALL")) {
res = 1;
} else if (!strcasecmp(dialstatus, "TORTURE")) {
res = 1;
} else if (!strcasecmp(dialstatus, "INVALIDARGS")) {
res = 1;
}
return res;
}
static void cel_dial_cb(void *data, struct stasis_subscription *sub,
struct stasis_message *message)
struct ast_multi_channel_blob *blob = stasis_message_data(message);
struct ast_channel_snapshot *snapshot;
snapshot = ast_multi_channel_blob_get_channel(blob, "caller");
if (!snapshot || cel_filter_channel_snapshot(snapshot)) {
if (!ast_strlen_zero(get_blob_variable(blob, "forward"))) {
struct ast_json *extra;
extra = ast_json_pack("{s: s}", "forward", get_blob_variable(blob, "forward"));
if (extra) {
cel_report_event(snapshot, AST_CEL_FORWARD, NULL, extra, NULL);
ast_json_unref(extra);
if (is_valid_dialstatus(blob)) {
save_dialstatus(blob, snapshot);
static void cel_generic_cb(
void *data, struct stasis_subscription *sub,
struct stasis_message *message)
{
struct ast_channel_blob *obj = stasis_message_data(message);
int event_type = ast_json_integer_get(ast_json_object_get(obj->blob, "event_type"));
struct ast_json *event_details = ast_json_object_get(obj->blob, "event_details");
switch (event_type) {
case AST_CEL_USER_DEFINED:
{
const char *event = ast_json_string_get(ast_json_object_get(event_details, "event"));
struct ast_json *extra = ast_json_object_get(event_details, "extra");
cel_report_event(obj->snapshot, event_type, event, extra, NULL);
break;
}
default:
ast_log(LOG_ERROR, "Unhandled %s event blob\n", ast_cel_get_type_name(event_type));
break;
}
}
static void cel_blind_transfer_cb(
void *data, struct stasis_subscription *sub,
struct stasis_message *message)
{
struct ast_blind_transfer_message *transfer_msg = stasis_message_data(message);
Mark Michelson
committed
struct ast_channel_snapshot *chan_snapshot = transfer_msg->transferer;
struct ast_bridge_snapshot *bridge_snapshot = transfer_msg->bridge;
struct ast_json *extra;
if (transfer_msg->result != AST_BRIDGE_TRANSFER_SUCCESS) {
extra = ast_json_pack("{s: s, s: s, s: s, s: s, s: s}",
"extension", transfer_msg->exten,
"context", transfer_msg->context,
"bridge_id", bridge_snapshot->uniqueid,
"transferee_channel_name", transfer_msg->transferee ? transfer_msg->transferee->name : "N/A",
"transferee_channel_uniqueid", transfer_msg->transferee ? transfer_msg->transferee->uniqueid : "N/A");
cel_report_event(chan_snapshot, AST_CEL_BLINDTRANSFER, NULL, extra, NULL);
ast_json_unref(extra);
}
}
static void cel_attended_transfer_cb(
void *data, struct stasis_subscription *sub,
struct stasis_message *message)
{
struct ast_attended_transfer_message *xfer = stasis_message_data(message);
struct ast_json *extra = NULL;
struct ast_bridge_snapshot *bridge1, *bridge2;
struct ast_channel_snapshot *channel1, *channel2;
/* Make sure bridge1 is always non-NULL */
if (!xfer->to_transferee.bridge_snapshot) {
bridge1 = xfer->to_transfer_target.bridge_snapshot;
bridge2 = xfer->to_transferee.bridge_snapshot;
channel1 = xfer->to_transfer_target.channel_snapshot;
channel2 = xfer->to_transferee.channel_snapshot;
} else {
bridge1 = xfer->to_transferee.bridge_snapshot;
bridge2 = xfer->to_transfer_target.bridge_snapshot;
channel1 = xfer->to_transferee.channel_snapshot;
channel2 = xfer->to_transfer_target.channel_snapshot;
}
switch (xfer->dest_type) {
case AST_ATTENDED_TRANSFER_DEST_FAIL:
return;
/* handle these three the same */
case AST_ATTENDED_TRANSFER_DEST_BRIDGE_MERGE:
case AST_ATTENDED_TRANSFER_DEST_LINK:
case AST_ATTENDED_TRANSFER_DEST_THREEWAY:
extra = ast_json_pack("{s: s, s: s, s: s, s: s, s: s, s: s, s: s, s: s}",
"bridge1_id", bridge1->uniqueid,
"channel2_name", channel2->name,
"channel2_uniqueid", channel2->uniqueid,
"bridge2_id", bridge2->uniqueid,
"transferee_channel_name", xfer->transferee ? xfer->transferee->name : "N/A",
"transferee_channel_uniqueid", xfer->transferee ? xfer->transferee->uniqueid : "N/A",
"transfer_target_channel_name", xfer->target ? xfer->target->name : "N/A",
"transfer_target_channel_uniqueid", xfer->target ? xfer->target->uniqueid : "N/A");
if (!extra) {
return;
}
break;
case AST_ATTENDED_TRANSFER_DEST_APP:
case AST_ATTENDED_TRANSFER_DEST_LOCAL_APP:
extra = ast_json_pack("{s: s, s: s, s: s, s: s, s: s, s: s, s: s, s: s}",
"bridge1_id", bridge1->uniqueid,
"channel2_name", channel2->name,
"channel2_uniqueid", channel2->uniqueid,
"app", xfer->dest.app,
"transferee_channel_name", xfer->transferee ? xfer->transferee->name : "N/A",
"transferee_channel_uniqueid", xfer->transferee ? xfer->transferee->uniqueid : "N/A",
"transfer_target_channel_name", xfer->target ? xfer->target->name : "N/A",
"transfer_target_channel_uniqueid", xfer->target ? xfer->target->uniqueid : "N/A");
if (!extra) {
return;
}
break;
}
cel_report_event(channel1, AST_CEL_ATTENDEDTRANSFER, NULL, extra, NULL);
ast_json_unref(extra);
}
static void cel_pickup_cb(
void *data, struct stasis_subscription *sub,
struct stasis_message *message)
{
struct ast_multi_channel_blob *obj = stasis_message_data(message);
struct ast_channel_snapshot *channel = ast_multi_channel_blob_get_channel(obj, "channel");
struct ast_channel_snapshot *target = ast_multi_channel_blob_get_channel(obj, "target");
struct ast_json *extra;
if (!channel || !target) {
return;
}
extra = ast_json_pack("{s: s, s: s}",
"pickup_channel", channel->name,
"pickup_channel_uniqueid", channel->uniqueid);
if (!extra) {
return;
}
cel_report_event(target, AST_CEL_PICKUP, NULL, extra, NULL);
ast_json_unref(extra);
static void cel_local_cb(
void *data, struct stasis_subscription *sub,
struct stasis_message *message)
{
struct ast_multi_channel_blob *obj = stasis_message_data(message);
struct ast_channel_snapshot *localone = ast_multi_channel_blob_get_channel(obj, "1");
struct ast_channel_snapshot *localtwo = ast_multi_channel_blob_get_channel(obj, "2");
struct ast_json *extra;
if (!localone || !localtwo) {
return;
}
extra = ast_json_pack("{s: s, s: s}",
"local_two", localtwo->name,
"local_two_uniqueid", localtwo->uniqueid);
if (!extra) {
return;
}
cel_report_event(localone, AST_CEL_LOCAL_OPTIMIZE, NULL, extra, NULL);
ast_json_unref(extra);
static void destroy_routes(void)
stasis_message_router_unsubscribe_and_join(cel_state_router);
cel_state_router = NULL;
static void destroy_subscriptions(void)
{
ao2_cleanup(cel_aggregation_topic);
cel_aggregation_topic = NULL;
ao2_cleanup(cel_topic);
cel_topic = NULL;
cel_channel_forwarder = stasis_forward_cancel(cel_channel_forwarder);
cel_bridge_forwarder = stasis_forward_cancel(cel_bridge_forwarder);
cel_parking_forwarder = stasis_forward_cancel(cel_parking_forwarder);
cel_cel_forwarder = stasis_forward_cancel(cel_cel_forwarder);
static void cel_engine_cleanup(void)
destroy_subscriptions();
STASIS_MESSAGE_TYPE_CLEANUP(cel_generic_type);
ast_cli_unregister(&cli_status);
aco_info_destroy(&cel_cfg_info);
ao2_global_obj_release(cel_configs);
ao2_global_obj_release(cel_dialstatus_store);
ao2_global_obj_release(cel_linkedids);
ao2_global_obj_release(cel_backends);
}
/*!
* \brief Create the Stasis subscriptions for CEL
*/
static int create_subscriptions(void)
cel_aggregation_topic = stasis_topic_create("cel_aggregation_topic");
if (!cel_aggregation_topic) {
return -1;
}
cel_topic = stasis_topic_create("cel_topic");
if (!cel_topic) {
return -1;
}
cel_channel_forwarder = stasis_forward_all(
ast_channel_topic_all_cached(),
if (!cel_channel_forwarder) {
return -1;
}
cel_bridge_forwarder = stasis_forward_all(
ast_bridge_topic_all_cached(),
if (!cel_bridge_forwarder) {
return -1;
}
cel_parking_forwarder = stasis_forward_all(
ast_parking_topic(),
if (!cel_parking_forwarder) {
return -1;
}
cel_cel_forwarder = stasis_forward_all(
ast_cel_topic(),
cel_aggregation_topic);
if (!cel_cel_forwarder) {
return -1;
}
return 0;
}
/*!
* \brief Create the Stasis message router and routes for CEL
*/
static int create_routes(void)
{
int ret = 0;
cel_state_router = stasis_message_router_create(cel_aggregation_topic);
if (!cel_state_router) {
return -1;
}
stasis_message_router_set_congestion_limits(cel_state_router, -1,
6 * AST_TASKPROCESSOR_HIGH_WATER_LEVEL);
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
ret |= stasis_message_router_add(cel_state_router,
stasis_cache_update_type(),
cel_snapshot_update_cb,
NULL);
ret |= stasis_message_router_add(cel_state_router,
ast_channel_dial_type(),
cel_dial_cb,
NULL);
ret |= stasis_message_router_add(cel_state_router,
ast_channel_entered_bridge_type(),
cel_bridge_enter_cb,
NULL);
ret |= stasis_message_router_add(cel_state_router,
ast_channel_left_bridge_type(),
cel_bridge_leave_cb,
NULL);
ret |= stasis_message_router_add(cel_state_router,
ast_parked_call_type(),
cel_parking_cb,
NULL);
ret |= stasis_message_router_add(cel_state_router,
cel_generic_type(),
cel_generic_cb,
NULL);
ret |= stasis_message_router_add(cel_state_router,
ast_blind_transfer_type(),
cel_blind_transfer_cb,
NULL);
ret |= stasis_message_router_add(cel_state_router,
ast_attended_transfer_type(),
cel_attended_transfer_cb,
NULL);
ret |= stasis_message_router_add(cel_state_router,
ast_call_pickup_type(),
cel_pickup_cb,
NULL);
ret |= stasis_message_router_add(cel_state_router,
ast_local_optimization_end_type(),
cel_local_cb,
NULL);
ast_log(AST_LOG_ERROR, "Failed to register for Stasis messages\n");
}
return ret;
}
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
static int lid_hash(const void *obj, const int flags)
{
const struct cel_linkedid *lid;
const char *key;
switch (flags & OBJ_SEARCH_MASK) {
case OBJ_SEARCH_KEY:
key = obj;
break;
case OBJ_SEARCH_OBJECT:
lid = obj;
key = lid->id;
break;
default:
/* Hash can only work on something with a full key. */
ast_assert(0);
return 0;
}
return ast_str_hash(key);
}
static int lid_cmp(void *obj, void *arg, int flags)
{
const struct cel_linkedid *object_left = obj;
const struct cel_linkedid *object_right = arg;
const char *right_key = arg;
int cmp;
switch (flags & OBJ_SEARCH_MASK) {
case OBJ_SEARCH_OBJECT:
right_key = object_right->id;
/* Fall through */
case OBJ_SEARCH_KEY:
cmp = strcmp(object_left->id, right_key);
break;
case OBJ_SEARCH_PARTIAL_KEY:
/*
* We could also use a partial key struct containing a length
* so strlen() does not get called for every comparison instead.
*/
cmp = strncmp(object_left->id, right_key, strlen(right_key));
break;
default:
/*
* What arg points to is specific to this traversal callback
* and has no special meaning to astobj2.
*/
cmp = 0;
break;
}
if (cmp) {
return 0;
}
/*
* At this point the traversal callback is identical to a sorted
* container.
*/
return CMP_MATCH;
}
int ast_cel_engine_init(void)
{
struct ao2_container *container;
container = ao2_container_alloc(NUM_APP_BUCKETS, lid_hash, lid_cmp);
ao2_global_obj_replace_unref(cel_linkedids, container);
ao2_cleanup(container);
if (!container) {
cel_engine_cleanup();
container = ao2_container_alloc(NUM_DIALSTATUS_BUCKETS,
dialstatus_hash, dialstatus_cmp);
ao2_global_obj_replace_unref(cel_dialstatus_store, container);
ao2_cleanup(container);
if (!container) {
cel_engine_cleanup();
return -1;
}
if (STASIS_MESSAGE_TYPE_INIT(cel_generic_type)) {
cel_engine_cleanup();
return -1;
}
if (ast_cli_register(&cli_status)) {
cel_engine_cleanup();
container = ao2_container_alloc(BACKEND_BUCKETS, cel_backend_hash, cel_backend_cmp);
ao2_global_obj_replace_unref(cel_backends, container);
ao2_cleanup(container);
if (!container) {
cel_engine_cleanup();
return -1;
}
if (aco_info_init(&cel_cfg_info)) {
cel_engine_cleanup();
return -1;
}
aco_option_register(&cel_cfg_info, "enable", ACO_EXACT, general_options, "no", OPT_BOOL_T, 1, FLDSET(struct ast_cel_general_config, enable));
aco_option_register(&cel_cfg_info, "dateformat", ACO_EXACT, general_options, "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_cel_general_config, date_format));
aco_option_register_custom(&cel_cfg_info, "apps", ACO_EXACT, general_options, "", apps_handler, 0);
aco_option_register_custom(&cel_cfg_info, "events", ACO_EXACT, general_options, "", events_handler, 0);
if (aco_process_config(&cel_cfg_info, 0)) {
struct cel_config *cel_cfg = cel_config_alloc();
if (!cel_cfg) {
cel_engine_cleanup();
return -1;
}
/* We couldn't process the configuration so create a default config. */
if (!aco_set_defaults(&general_option, "general", cel_cfg->general)) {
ast_log(LOG_NOTICE, "Failed to process CEL configuration; using defaults\n");
ao2_global_obj_replace_unref(cel_configs, cel_cfg);
ao2_ref(cel_cfg, -1);
cel_engine_cleanup();
return -1;
}
if (ast_cel_check_enabled() && create_routes()) {
cel_engine_cleanup();
ast_register_cleanup(cel_engine_cleanup);
int ast_cel_engine_reload(void)
{
unsigned int was_enabled = ast_cel_check_enabled();
unsigned int is_enabled;
if (aco_process_config(&cel_cfg_info, 1) == ACO_PROCESS_ERROR) {
return -1;
}
is_enabled = ast_cel_check_enabled();
if (!was_enabled && is_enabled) {
return -1;
}
} else if (was_enabled && !is_enabled) {
}
ast_verb(3, "CEL logging %sabled.\n", is_enabled ? "en" : "dis");
return 0;
}
void ast_cel_publish_event(struct ast_channel *chan,
enum ast_cel_event_type event_type,
struct ast_json *blob)
{
struct ast_json *cel_blob;
struct stasis_message *message;
cel_blob = ast_json_pack("{s: i, s: o}",
"event_details", ast_json_ref(blob));
message = ast_channel_blob_create_from_cache(ast_channel_uniqueid(chan), cel_generic_type(), cel_blob);
if (message) {
stasis_publish(ast_cel_topic(), message);
}
ao2_cleanup(message);
ast_json_unref(cel_blob);
}
struct stasis_topic *ast_cel_topic(void)
{
return cel_topic;
}
struct ast_cel_general_config *ast_cel_get_config(void)
{
RAII_VAR(struct cel_config *, mod_cfg, ao2_global_obj_ref(cel_configs), ao2_cleanup);
ao2_ref(mod_cfg->general, +1);
return mod_cfg->general;
}
void ast_cel_set_config(struct ast_cel_general_config *config)
{
int was_enabled;
struct ast_cel_general_config *cleanup_config;
struct cel_config *mod_cfg = ao2_global_obj_ref(cel_configs);
was_enabled = ast_cel_check_enabled();
cleanup_config = mod_cfg->general;
ao2_bump(config);
ao2_cleanup(cleanup_config);
is_enabled = ast_cel_check_enabled();
if (!was_enabled && is_enabled) {
} else if (was_enabled && !is_enabled) {
ao2_ref(mod_cfg, -1);
int ast_cel_backend_unregister(const char *name)
{
struct ao2_container *backends = ao2_global_obj_ref(cel_backends);
if (backends) {
ao2_find(backends, name, OBJ_SEARCH_KEY | OBJ_NODATA | OBJ_UNLINK);
ao2_ref(backends, -1);
}
return 0;
}
int ast_cel_backend_register(const char *name, ast_cel_backend_cb backend_callback)
{
RAII_VAR(struct ao2_container *, backends, ao2_global_obj_ref(cel_backends), ao2_cleanup);
struct cel_backend *backend;
if (!backends || ast_strlen_zero(name) || !backend_callback) {
return -1;
}
/* The backend object is immutable so it doesn't need a lock of its own. */
backend = ao2_alloc_options(sizeof(*backend) + 1 + strlen(name), NULL,
AO2_ALLOC_OPT_LOCK_NOLOCK);
if (!backend) {
return -1;
}
strcpy(backend->name, name);/* Safe */
backend->callback = backend_callback;
ao2_link(backends, backend);
ao2_ref(backend, -1);