#include "asterisk.h"
-ASTERISK_REGISTER_FILE()
-
#include <signal.h>
#include <inttypes.h>
#include "asterisk/cdr.h"
#include "asterisk/callerid.h"
#include "asterisk/manager.h"
+#include "asterisk/module.h"
#include "asterisk/causes.h"
#include "asterisk/linkedlists.h"
#include "asterisk/utils.h"
#include "asterisk/config.h"
#include "asterisk/cli.h"
#include "asterisk/stringfields.h"
-#include "asterisk/data.h"
#include "asterisk/config_options.h"
#include "asterisk/json.h"
#include "asterisk/parking.h"
#include "asterisk/stasis_bridges.h"
#include "asterisk/stasis_message_router.h"
#include "asterisk/astobj2.h"
+#include "asterisk/taskprocessor.h"
/*** DOCUMENTATION
<configInfo name="cdr" language="en_US">
#define DEFAULT_BATCH_SCHEDULER_ONLY "0"
#define DEFAULT_BATCH_SAFE_SHUTDOWN "1"
-#define CDR_DEBUG(mod_cfg, fmt, ...) \
+#define cdr_set_debug_mode(mod_cfg) \
+ do { \
+ cdr_debug_enabled = ast_test_flag(&(mod_cfg)->general->settings, CDR_DEBUG); \
+ } while (0)
+
+static int cdr_debug_enabled;
+
+#define CDR_DEBUG(fmt, ...) \
do { \
- if (ast_test_flag(&(mod_cfg)->general->settings, CDR_DEBUG)) { \
+ if (cdr_debug_enabled) { \
ast_verbose((fmt), ##__VA_ARGS__); \
} \
} while (0)
/*! \brief The configuration settings for this module */
struct module_config {
- struct ast_cdr_config *general; /*< CDR global settings */
+ struct ast_cdr_config *general; /*!< CDR global settings */
};
/*! \brief The container for the module configuration */
.type = ACO_GLOBAL,
.name = "general",
.item_offset = offsetof(struct module_config, general),
- .category = "^general$",
- .category_match = ACO_WHITELIST,
+ .category = "general",
+ .category_match = ACO_WHITELIST_EXACT,
+};
+
+/*! Config sections used by existing modules. Do not add to this list. */
+static const char *ignore_categories[] = {
+ "csv",
+ "custom",
+ "manager",
+ "odbc",
+ "pgsql",
+ "radius",
+ "sqlite",
+ "tds",
+ "mysql",
+ NULL,
+};
+
+static struct aco_type ignore_option = {
+ .type = ACO_IGNORE,
+ .name = "modules",
+ .category = (const char*)ignore_categories,
+ .category_match = ACO_WHITELIST_ARRAY,
};
static void *module_config_alloc(void);
static void module_config_destructor(void *obj);
+static void module_config_post_apply(void);
/*! \brief The file definition */
static struct aco_file module_file_conf = {
.filename = "cdr.conf",
- .skip_category = "(^csv$|^custom$|^manager$|^odbc$|^pgsql$|^radius$|^sqlite$|^tds$|^mysql$)",
- .types = ACO_TYPES(&general_option),
+ .types = ACO_TYPES(&general_option, &ignore_option),
};
CONFIG_INFO_CORE("cdr", cfg_info, module_configs, module_config_alloc,
.files = ACO_FILES(&module_file_conf),
+ .post_apply_config = module_config_post_apply,
);
static struct aco_type *general_options[] = ACO_TYPES(&general_option);
+static void module_config_post_apply(void)
+{
+ struct module_config *mod_cfg;
+
+ mod_cfg = ao2_global_obj_ref(module_configs);
+ if (!mod_cfg) {
+ return;
+ }
+ cdr_set_debug_mode(mod_cfg);
+ ao2_cleanup(mod_cfg);
+}
+
/*! \brief Dispose of a module config object */
static void module_config_destructor(void *obj)
{
/*! \brief List of registered backends */
static AST_RWLIST_HEAD_STATIC(be_list, cdr_beitem);
+/*! \brief List of registered modifiers */
+static AST_RWLIST_HEAD_STATIC(mo_list, cdr_beitem);
+
/*! \brief Queued CDR waiting to be batched */
struct cdr_batch_item {
struct ast_cdr *cdr;
AST_MUTEX_DEFINE_STATIC(cdr_pending_lock);
static ast_cond_t cdr_pending_cond;
-/*! \brief A container of the active CDRs indexed by Party A channel id */
-static struct ao2_container *active_cdrs_by_channel;
+/*! \brief A container of the active master CDRs indexed by Party A channel uniqueid */
+static struct ao2_container *active_cdrs_master;
+
+/*! \brief A container of all active CDRs with a Party B indexed by Party B channel name */
+static struct ao2_container *active_cdrs_all;
/*! \brief Message router for stasis messages regarding channel state */
static struct stasis_message_router *stasis_router;
struct ast_flags flags; /*!< Flags on the CDR */
AST_DECLARE_STRING_FIELDS(
AST_STRING_FIELD(linkedid); /*!< Linked ID. Cached here as it may change out from party A, which must be immutable */
- AST_STRING_FIELD(uniqueid); /*!< Unique id of party A. Cached here as it is the primary key of this CDR */
+ AST_STRING_FIELD(uniqueid); /*!< Unique id of party A. Cached here as it is the master CDR container key */
AST_STRING_FIELD(name); /*!< Channel name of party A. Cached here as the party A address may change */
AST_STRING_FIELD(bridge); /*!< The bridge the party A happens to be in. */
AST_STRING_FIELD(appl); /*!< The last accepted application party A was in */
AST_STRING_FIELD(data); /*!< The data for the last accepted application party A was in */
AST_STRING_FIELD(context); /*!< The accepted context for Party A */
AST_STRING_FIELD(exten); /*!< The accepted extension for Party A */
+ AST_STRING_FIELD(party_b_name); /*!< Party B channel name. Cached here as it is the all CDRs container key */
);
struct cdr_object *next; /*!< The next CDR object in the chain */
struct cdr_object *last; /*!< The last CDR object in the chain */
+ int is_root; /*!< True if this is the first CDR in the chain */
};
/*!
*/
static void cdr_object_snapshot_copy(struct cdr_object_snapshot *dst, struct cdr_object_snapshot *src)
{
- if (dst->snapshot) {
- ao2_t_ref(dst->snapshot, -1, "release old snapshot during copy");
- }
- dst->snapshot = src->snapshot;
- ao2_t_ref(dst->snapshot, +1, "bump new snapshot during copy");
+ ao2_t_replace(dst->snapshot, src->snapshot, "CDR snapshot copy");
strcpy(dst->userfield, src->userfield);
dst->flags = src->flags;
copy_variables(&dst->variables, &src->variables);
*/
static void cdr_object_transition_state(struct cdr_object *cdr, struct cdr_object_fn_table *fn_table)
{
- RAII_VAR(struct module_config *, mod_cfg, ao2_global_obj_ref(module_configs), ao2_cleanup);
-
- CDR_DEBUG(mod_cfg, "%p - Transitioning CDR for %s from state %s to %s\n",
+ CDR_DEBUG("%p - Transitioning CDR for %s from state %s to %s\n",
cdr, cdr->party_a.snapshot->name,
cdr->fn_table ? cdr->fn_table->name : "NONE", fn_table->name);
cdr->fn_table = fn_table;
cdr->fn_table->init_function(cdr);
}
}
-/*! \internal
- * \brief Hash function for containers of CDRs indexing by Party A uniqueid */
-static int cdr_object_channel_hash_fn(const void *obj, const int flags)
+
+/*!
+ * \internal
+ * \brief Hash function for master CDR container indexed by Party A uniqueid.
+ */
+static int cdr_master_hash_fn(const void *obj, const int flags)
{
const struct cdr_object *cdr;
const char *key;
- switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
- case OBJ_KEY:
+ switch (flags & OBJ_SEARCH_MASK) {
+ case OBJ_SEARCH_KEY:
key = obj;
break;
- case OBJ_POINTER:
+ case OBJ_SEARCH_OBJECT:
cdr = obj;
key = cdr->uniqueid;
break;
return ast_str_case_hash(key);
}
-/*! \internal
- * \brief Comparison function for containers of CDRs indexing by Party A uniqueid
+/*!
+ * \internal
+ * \brief Comparison function for master CDR container indexed by Party A uniqueid.
*/
-static int cdr_object_channel_cmp_fn(void *obj, void *arg, int flags)
+static int cdr_master_cmp_fn(void *obj, void *arg, int flags)
{
struct cdr_object *left = obj;
struct cdr_object *right = arg;
const char *right_key = arg;
int cmp;
- switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
- case OBJ_POINTER:
+ switch (flags & OBJ_SEARCH_MASK) {
+ case OBJ_SEARCH_OBJECT:
right_key = right->uniqueid;
/* Fall through */
- case OBJ_KEY:
+ case OBJ_SEARCH_KEY:
cmp = strcmp(left->uniqueid, right_key);
break;
- case OBJ_PARTIAL_KEY:
+ 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.
}
/*!
+ * \internal
+ * \brief Hash function for all CDR container indexed by Party B channel name.
+ */
+static int cdr_all_hash_fn(const void *obj, const int flags)
+{
+ const struct cdr_object *cdr;
+ const char *key;
+
+ switch (flags & OBJ_SEARCH_MASK) {
+ case OBJ_SEARCH_KEY:
+ key = obj;
+ break;
+ case OBJ_SEARCH_OBJECT:
+ cdr = obj;
+ key = cdr->party_b_name;
+ break;
+ default:
+ ast_assert(0);
+ return 0;
+ }
+ return ast_str_case_hash(key);
+}
+
+/*!
+ * \internal
+ * \brief Comparison function for all CDR container indexed by Party B channel name.
+ */
+static int cdr_all_cmp_fn(void *obj, void *arg, int flags)
+{
+ struct cdr_object *left = obj;
+ struct cdr_object *right = arg;
+ const char *right_key = arg;
+ int cmp;
+
+ switch (flags & OBJ_SEARCH_MASK) {
+ case OBJ_SEARCH_OBJECT:
+ right_key = right->party_b_name;
+ /* Fall through */
+ case OBJ_SEARCH_KEY:
+ cmp = strcasecmp(left->party_b_name, 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 = strncasecmp(left->party_b_name, right_key, strlen(right_key));
+ break;
+ default:
+ /* Sort can only work on something with a full or partial key. */
+ ast_assert(0);
+ cmp = 0;
+ break;
+ }
+ return cmp ? 0 : CMP_MATCH;
+}
+
+/*!
+ * \internal
+ * \brief Relink the CDR because Party B's snapshot changed.
+ * \since 13.19.0
+ *
+ * \return Nothing
+ */
+static void cdr_all_relink(struct cdr_object *cdr)
+{
+ ao2_lock(active_cdrs_all);
+ if (cdr->party_b.snapshot) {
+ if (strcasecmp(cdr->party_b_name, cdr->party_b.snapshot->name)) {
+ ao2_unlink_flags(active_cdrs_all, cdr, OBJ_NOLOCK);
+ ast_string_field_set(cdr, party_b_name, cdr->party_b.snapshot->name);
+ ao2_link_flags(active_cdrs_all, cdr, OBJ_NOLOCK);
+ }
+ } else {
+ ao2_unlink_flags(active_cdrs_all, cdr, OBJ_NOLOCK);
+ ast_string_field_set(cdr, party_b_name, "");
+ }
+ ao2_unlock(active_cdrs_all);
+}
+
+/*!
+ * \internal
+ * \brief Unlink the master CDR and chained records from the active_cdrs_all container.
+ * \since 13.19.0
+ *
+ * \return Nothing
+ */
+static void cdr_all_unlink(struct cdr_object *cdr)
+{
+ struct cdr_object *cur;
+ struct cdr_object *next;
+
+ ast_assert(cdr->is_root);
+
+ /* Hold a ref to the root CDR to ensure the list members don't go away on us. */
+ ao2_ref(cdr, +1);
+ ao2_lock(active_cdrs_all);
+ for (cur = cdr; cur; cur = next) {
+ next = cur->next;
+ ao2_unlink_flags(active_cdrs_all, cur, OBJ_NOLOCK);
+ /*
+ * It is safe to still use cur after unlinking because the
+ * root CDR holds a ref to all the CDRs in the list and we
+ * have a ref to the root CDR.
+ */
+ ast_string_field_set(cur, party_b_name, "");
+ }
+ ao2_unlock(active_cdrs_all);
+ ao2_ref(cdr, -1);
+}
+
+/*!
* \brief \ref cdr_object Destructor
*/
static void cdr_object_dtor(void *obj)
}
ast_string_field_free_memory(cdr);
- ao2_cleanup(cdr->next);
+ /* CDR destruction used to work by calling ao2_cleanup(next) and
+ * allowing the chain to destroy itself neatly. Unfortunately, for
+ * really long chains, this can result in a stack overflow. So now
+ * when the root CDR is destroyed, it is responsible for unreffing
+ * all CDRs in the chain
+ */
+ if (cdr->is_root) {
+ struct cdr_object *curr = cdr->next;
+ struct cdr_object *next;
+
+ while (curr) {
+ next = curr->next;
+ ao2_cleanup(curr);
+ curr = next;
+ }
+ }
}
/*!
*/
static struct cdr_object *cdr_object_alloc(struct ast_channel_snapshot *chan)
{
- RAII_VAR(struct module_config *, mod_cfg, ao2_global_obj_ref(module_configs), ao2_cleanup);
struct cdr_object *cdr;
ast_assert(chan != NULL);
cdr->party_a.snapshot = chan;
ao2_t_ref(cdr->party_a.snapshot, +1, "bump snapshot during CDR creation");
- CDR_DEBUG(mod_cfg, "%p - Created CDR for channel %s\n", cdr, chan->name);
+ CDR_DEBUG("%p - Created CDR for channel %s\n", cdr, chan->name);
cdr_object_transition_state(cdr, &single_state_fn_table);
}
/*!
+ * \internal
+ * \brief Determine if CDR flag is configured.
+ *
+ * \param cdr_flag The configured CDR flag to check.
+ *
+ * \retval 0 if the CDR flag is not configured.
+ * \retval non-zero if the CDR flag is configured.
+ *
+ * \return Nothing
+ */
+static int is_cdr_flag_set(unsigned int cdr_flag)
+{
+ struct module_config *mod_cfg;
+ int flag_set;
+
+ mod_cfg = ao2_global_obj_ref(module_configs);
+ flag_set = mod_cfg && ast_test_flag(&mod_cfg->general->settings, cdr_flag);
+ ao2_cleanup(mod_cfg);
+ return flag_set;
+}
+
+/*!
* \brief Return whether or not a channel has changed its state in the dialplan, subject
* to endbeforehexten logic
*
static int snapshot_cep_changed(struct ast_channel_snapshot *old_snapshot,
struct ast_channel_snapshot *new_snapshot)
{
- RAII_VAR(struct module_config *, mod_cfg,
- ao2_global_obj_ref(module_configs), ao2_cleanup);
-
/* If we ignore hangup logic, don't indicate that we're executing anything new */
- if (ast_test_flag(&mod_cfg->general->settings, CDR_END_BEFORE_H_EXTEN)
- && ast_test_flag(&new_snapshot->softhangup_flags, AST_SOFTHANGUP_HANGUP_EXEC)) {
+ if (ast_test_flag(&new_snapshot->softhangup_flags, AST_SOFTHANGUP_HANGUP_EXEC)
+ && is_cdr_flag_set(CDR_END_BEFORE_H_EXTEN)) {
return 0;
}
* will attempt to clear the application and restore the dummy originate application
* of "AppDialX". Ignore application changes to AppDialX as a result.
*/
- if (strcmp(new_snapshot->appl, old_snapshot->appl) && strncasecmp(new_snapshot->appl, "appdial", 7)
+ if (strcmp(new_snapshot->appl, old_snapshot->appl)
+ && strncasecmp(new_snapshot->appl, "appdial", 7)
&& (strcmp(new_snapshot->context, old_snapshot->context)
- || strcmp(new_snapshot->exten, old_snapshot->exten)
- || new_snapshot->priority != old_snapshot->priority)) {
+ || strcmp(new_snapshot->exten, old_snapshot->exten)
+ || new_snapshot->priority != old_snapshot->priority)) {
return 1;
}
*/
static long cdr_object_get_billsec(struct cdr_object *cdr)
{
- RAII_VAR(struct module_config *, mod_cfg, ao2_global_obj_ref(module_configs), ao2_cleanup);
long int ms;
if (ast_tvzero(cdr->answer)) {
return 0;
}
+
ms = ast_tvdiff_ms(ast_tvzero(cdr->end) ? ast_tvnow() : cdr->end, cdr->answer);
- if (ast_test_flag(&mod_cfg->general->settings, CDR_INITIATED_SECONDS)
- && (ms % 1000 >= 500)) {
+ if (ms % 1000 >= 500
+ && is_cdr_flag_set(CDR_INITIATED_SECONDS)) {
ms = (ms / 1000) + 1;
} else {
ms = ms / 1000;
*/
static void cdr_object_dispatch(struct cdr_object *cdr)
{
- RAII_VAR(struct module_config *, mod_cfg,
- ao2_global_obj_ref(module_configs), ao2_cleanup);
struct ast_cdr *pub_cdr;
- CDR_DEBUG(mod_cfg, "%p - Dispatching CDR for Party A %s, Party B %s\n", cdr,
- cdr->party_a.snapshot->name,
- cdr->party_b.snapshot ? cdr->party_b.snapshot->name : "<none>");
+ CDR_DEBUG("%p - Dispatching CDR for Party A %s, Party B %s\n", cdr,
+ cdr->party_a.snapshot->name,
+ cdr->party_b.snapshot ? cdr->party_b.snapshot->name : "<none>");
pub_cdr = cdr_object_create_public_records(cdr);
cdr_detach(pub_cdr);
}
*/
static void cdr_object_set_disposition(struct cdr_object *cdr, int hangupcause)
{
- RAII_VAR(struct module_config *, mod_cfg,
- ao2_global_obj_ref(module_configs), ao2_cleanup);
-
/* Change the disposition based on the hang up cause */
switch (hangupcause) {
case AST_CAUSE_BUSY:
cdr->disposition = AST_CDR_BUSY;
break;
case AST_CAUSE_CONGESTION:
- if (!ast_test_flag(&mod_cfg->general->settings, CDR_CONGESTION)) {
+ if (!is_cdr_flag_set(CDR_CONGESTION)) {
cdr->disposition = AST_CDR_FAILED;
} else {
cdr->disposition = AST_CDR_CONGESTION;
*/
static void cdr_object_check_party_a_hangup(struct cdr_object *cdr)
{
- RAII_VAR(struct module_config *, mod_cfg, ao2_global_obj_ref(module_configs), ao2_cleanup);
-
- if (ast_test_flag(&mod_cfg->general->settings, CDR_END_BEFORE_H_EXTEN)
- && ast_test_flag(&cdr->party_a.snapshot->softhangup_flags, AST_SOFTHANGUP_HANGUP_EXEC)) {
+ if (ast_test_flag(&cdr->party_a.snapshot->softhangup_flags, AST_SOFTHANGUP_HANGUP_EXEC)
+ && is_cdr_flag_set(CDR_END_BEFORE_H_EXTEN)) {
cdr_object_finalize(cdr);
}
* \brief Check to see if a CDR needs to be answered based on its Party A.
* Note that this is safe to call as much as you want - we won't answer twice
*/
-static void cdr_object_check_party_a_answer(struct cdr_object *cdr) {
- RAII_VAR(struct module_config *, mod_cfg, ao2_global_obj_ref(module_configs), ao2_cleanup);
-
+static void cdr_object_check_party_a_answer(struct cdr_object *cdr)
+{
if (cdr->party_a.snapshot->state == AST_STATE_UP && ast_tvzero(cdr->answer)) {
cdr->answer = ast_tvnow();
/* tv_usec is suseconds_t, which could be int or long */
- CDR_DEBUG(mod_cfg, "%p - Set answered time to %ld.%06ld\n", cdr,
+ CDR_DEBUG("%p - Set answered time to %ld.%06ld\n", cdr,
(long)cdr->answer.tv_sec,
(long)cdr->answer.tv_usec);
}
set_variable(&old_snapshot->variables, "calledsubaddr", new_snapshot->dialed_subaddr);
return;
}
- if (!strcmp(old_snapshot->snapshot->caller_dnid, new_snapshot->caller_dnid)) {
+ if (strcmp(old_snapshot->snapshot->caller_dnid, new_snapshot->caller_dnid)) {
set_variable(&old_snapshot->variables, "dnid", new_snapshot->caller_dnid);
}
- if (!strcmp(old_snapshot->snapshot->caller_subaddr, new_snapshot->caller_subaddr)) {
+ if (strcmp(old_snapshot->snapshot->caller_subaddr, new_snapshot->caller_subaddr)) {
set_variable(&old_snapshot->variables, "callingsubaddr", new_snapshot->caller_subaddr);
}
- if (!strcmp(old_snapshot->snapshot->dialed_subaddr, new_snapshot->dialed_subaddr)) {
+ if (strcmp(old_snapshot->snapshot->dialed_subaddr, new_snapshot->dialed_subaddr)) {
set_variable(&old_snapshot->variables, "calledsubaddr", new_snapshot->dialed_subaddr);
}
}
struct ast_channel_snapshot *new_snapshot)
{
cdr_object_update_cid(old_snapshot, new_snapshot);
- if (old_snapshot->snapshot) {
- ao2_t_ref(old_snapshot->snapshot, -1, "Drop ref for swap");
- }
- ao2_t_ref(new_snapshot, +1, "Bump ref for swap");
- old_snapshot->snapshot = new_snapshot;
+ ao2_t_replace(old_snapshot->snapshot, new_snapshot, "Swap CDR shapshot");
}
/* BASE METHOD IMPLEMENTATIONS */
static int base_process_party_a(struct cdr_object *cdr, struct ast_channel_snapshot *snapshot)
{
- RAII_VAR(struct module_config *, mod_cfg, ao2_global_obj_ref(module_configs), ao2_cleanup);
-
ast_assert(strcasecmp(snapshot->name, cdr->party_a.snapshot->name) == 0);
- /* Ignore any snapshots from a dead or dying channel */
+ /* Finalize the CDR if we're in hangup logic and we're set to do so */
if (ast_test_flag(&snapshot->softhangup_flags, AST_SOFTHANGUP_HANGUP_EXEC)
- && ast_test_flag(&mod_cfg->general->settings, CDR_END_BEFORE_H_EXTEN)) {
- cdr_object_check_party_a_hangup(cdr);
+ && is_cdr_flag_set(CDR_END_BEFORE_H_EXTEN)) {
+ cdr_object_finalize(cdr);
return 0;
}
*/
if (!ast_test_flag(&snapshot->flags, AST_FLAG_SUBROUTINE_EXEC)
|| ast_test_flag(&snapshot->softhangup_flags, AST_SOFTHANGUP_HANGUP_EXEC)) {
- ast_string_field_set(cdr, context, snapshot->context);
- ast_string_field_set(cdr, exten, snapshot->exten);
+ if (strcmp(cdr->context, snapshot->context)) {
+ ast_string_field_set(cdr, context, snapshot->context);
+ }
+ if (strcmp(cdr->exten, snapshot->exten)) {
+ ast_string_field_set(cdr, exten, snapshot->exten);
+ }
}
cdr_object_swap_snapshot(&cdr->party_a, snapshot);
* of "AppDialX". Prevent that, and any other application changes we might not want
* here.
*/
- if (!ast_strlen_zero(snapshot->appl)
- && (strncasecmp(snapshot->appl, "appdial", 7) || ast_strlen_zero(cdr->appl))
- && !ast_test_flag(&cdr->flags, AST_CDR_LOCK_APP)) {
- ast_string_field_set(cdr, appl, snapshot->appl);
- ast_string_field_set(cdr, data, snapshot->data);
+ if (!ast_test_flag(&cdr->flags, AST_CDR_LOCK_APP)
+ && !ast_strlen_zero(snapshot->appl)
+ && (strncasecmp(snapshot->appl, "appdial", 7) || ast_strlen_zero(cdr->appl))) {
+ if (strcmp(cdr->appl, snapshot->appl)) {
+ ast_string_field_set(cdr, appl, snapshot->appl);
+ }
+ if (strcmp(cdr->data, snapshot->data)) {
+ ast_string_field_set(cdr, data, snapshot->data);
+ }
/* Dial (app_dial) is a special case. Because pre-dial handlers, which
* execute before the dial begins, will alter the application/data to
}
}
- ast_string_field_set(cdr, linkedid, snapshot->linkedid);
+ if (strcmp(cdr->linkedid, snapshot->linkedid)) {
+ ast_string_field_set(cdr, linkedid, snapshot->linkedid);
+ }
cdr_object_check_party_a_answer(cdr);
cdr_object_check_party_a_hangup(cdr);
static int base_process_bridge_leave(struct cdr_object *cdr, struct ast_bridge_snapshot *bridge, struct ast_channel_snapshot *channel)
{
- /* In general, most things shouldn't get a bridge leave */
- ast_assert(0);
- return 1;
+ return 0;
}
static int base_process_dial_end(struct cdr_object *cdr, struct ast_channel_snapshot *caller, struct ast_channel_snapshot *peer, const char *dial_status)
{
- /* In general, most things shouldn't get a dial end. */
- ast_assert(0);
return 0;
}
/* SINGLE STATE */
-static void single_state_init_function(struct cdr_object *cdr) {
+static void single_state_init_function(struct cdr_object *cdr)
+{
cdr->start = ast_tvnow();
cdr_object_check_party_a_answer(cdr);
}
static int single_state_process_dial_begin(struct cdr_object *cdr, struct ast_channel_snapshot *caller, struct ast_channel_snapshot *peer)
{
- RAII_VAR(struct module_config *, mod_cfg, ao2_global_obj_ref(module_configs), ao2_cleanup);
-
if (caller && !strcasecmp(cdr->party_a.snapshot->name, caller->name)) {
base_process_party_a(cdr, caller);
- CDR_DEBUG(mod_cfg, "%p - Updated Party A %s snapshot\n", cdr,
- cdr->party_a.snapshot->name);
+ CDR_DEBUG("%p - Updated Party A %s snapshot\n", cdr,
+ cdr->party_a.snapshot->name);
cdr_object_swap_snapshot(&cdr->party_b, peer);
- CDR_DEBUG(mod_cfg, "%p - Updated Party B %s snapshot\n", cdr,
- cdr->party_b.snapshot->name);
+ cdr_all_relink(cdr);
+ CDR_DEBUG("%p - Updated Party B %s snapshot\n", cdr,
+ cdr->party_b.snapshot->name);
/* If we have two parties, lock the application that caused the
* two parties to be associated. This prevents mid-call event
} else if (!strcasecmp(cdr->party_a.snapshot->name, peer->name)) {
/* We're the entity being dialed, i.e., outbound origination */
base_process_party_a(cdr, peer);
- CDR_DEBUG(mod_cfg, "%p - Updated Party A %s snapshot\n", cdr,
- cdr->party_a.snapshot->name);
+ CDR_DEBUG("%p - Updated Party A %s snapshot\n", cdr,
+ cdr->party_a.snapshot->name);
}
cdr_object_transition_state(cdr, &dial_state_fn_table);
static int single_state_bridge_enter_comparison(struct cdr_object *cdr,
struct cdr_object *cand_cdr)
{
- RAII_VAR(struct module_config *, mod_cfg, ao2_global_obj_ref(module_configs), ao2_cleanup);
struct cdr_object_snapshot *party_a;
/* Don't match on ourselves */
/* Try the candidate CDR's Party A first */
party_a = cdr_object_pick_party_a(&cdr->party_a, &cand_cdr->party_a);
if (!strcasecmp(party_a->snapshot->name, cdr->party_a.snapshot->name)) {
- CDR_DEBUG(mod_cfg, "%p - Party A %s has new Party B %s\n",
+ CDR_DEBUG("%p - Party A %s has new Party B %s\n",
cdr, cdr->party_a.snapshot->name, cand_cdr->party_a.snapshot->name);
cdr_object_snapshot_copy(&cdr->party_b, &cand_cdr->party_a);
+ cdr_all_relink(cdr);
if (!cand_cdr->party_b.snapshot) {
/* We just stole them - finalize their CDR. Note that this won't
* transition their state, it just sets the end time and the
}
party_a = cdr_object_pick_party_a(&cdr->party_a, &cand_cdr->party_b);
if (!strcasecmp(party_a->snapshot->name, cdr->party_a.snapshot->name)) {
- CDR_DEBUG(mod_cfg, "%p - Party A %s has new Party B %s\n",
+ CDR_DEBUG("%p - Party A %s has new Party B %s\n",
cdr, cdr->party_a.snapshot->name, cand_cdr->party_b.snapshot->name);
cdr_object_snapshot_copy(&cdr->party_b, &cand_cdr->party_b);
+ cdr_all_relink(cdr);
return 0;
}
for (it_cdrs = ao2_iterator_init(bridge->channels, 0);
!success && (channel_id = ao2_iterator_next(&it_cdrs));
ao2_ref(channel_id, -1)) {
- RAII_VAR(struct cdr_object *, cand_cdr_master,
- ao2_find(active_cdrs_by_channel, channel_id, OBJ_KEY),
- ao2_cleanup);
+ struct cdr_object *cand_cdr_master;
struct cdr_object *cand_cdr;
+ cand_cdr_master = ao2_find(active_cdrs_master, channel_id, OBJ_SEARCH_KEY);
if (!cand_cdr_master) {
continue;
}
break;
}
ao2_unlock(cand_cdr_master);
+ ao2_cleanup(cand_cdr_master);
}
ao2_iterator_destroy(&it_cdrs);
static void dial_state_process_party_b(struct cdr_object *cdr, struct ast_channel_snapshot *snapshot)
{
ast_assert(snapshot != NULL);
+ ast_assert(cdr->party_b.snapshot
+ && !strcasecmp(cdr->party_b.snapshot->name, snapshot->name));
- if (!cdr->party_b.snapshot
- || strcasecmp(cdr->party_b.snapshot->name, snapshot->name)) {
- return;
- }
cdr_object_swap_snapshot(&cdr->party_b, snapshot);
/* If party B hangs up, finalize this CDR */
*/
static enum ast_cdr_disposition dial_status_to_disposition(const char *dial_status)
{
- RAII_VAR(struct module_config *, mod_cfg,
- ao2_global_obj_ref(module_configs), ao2_cleanup);
-
if (!strcmp(dial_status, "ANSWER")) {
return AST_CDR_ANSWERED;
} else if (!strcmp(dial_status, "BUSY")) {
} else if (!strcmp(dial_status, "CANCEL") || !strcmp(dial_status, "NOANSWER")) {
return AST_CDR_NOANSWER;
} else if (!strcmp(dial_status, "CONGESTION")) {
- if (!ast_test_flag(&mod_cfg->general->settings, CDR_CONGESTION)) {
+ if (!is_cdr_flag_set(CDR_CONGESTION)) {
return AST_CDR_FAILED;
} else {
return AST_CDR_CONGESTION;
static enum process_bridge_enter_results dial_state_process_bridge_enter(struct cdr_object *cdr, struct ast_bridge_snapshot *bridge, struct ast_channel_snapshot *channel)
{
- struct ao2_iterator it_cdrs;
- char *channel_id;
int success = 0;
ast_string_field_set(cdr, bridge, bridge->uniqueid);
return BRIDGE_ENTER_ONLY_PARTY;
}
- for (it_cdrs = ao2_iterator_init(bridge->channels, 0);
- !success && (channel_id = ao2_iterator_next(&it_cdrs));
- ao2_ref(channel_id, -1)) {
- RAII_VAR(struct cdr_object *, cand_cdr_master,
- ao2_find(active_cdrs_by_channel, channel_id, OBJ_KEY),
- ao2_cleanup);
- struct cdr_object *cand_cdr;
+ /* If we don't have a Party B (originated channel), skip it */
+ if (cdr->party_b.snapshot) {
+ struct ao2_iterator it_cdrs;
+ char *channel_id;
- if (!cand_cdr_master) {
- continue;
- }
+ for (it_cdrs = ao2_iterator_init(bridge->channels, 0);
+ !success && (channel_id = ao2_iterator_next(&it_cdrs));
+ ao2_ref(channel_id, -1)) {
+ struct cdr_object *cand_cdr_master;
+ struct cdr_object *cand_cdr;
- ao2_lock(cand_cdr_master);
- for (cand_cdr = cand_cdr_master; cand_cdr; cand_cdr = cand_cdr->next) {
- /* Skip any records that are not in a bridge or in this bridge.
- * I'm not sure how that would happen, but it pays to be careful. */
- if (cand_cdr->fn_table != &bridge_state_fn_table ||
- strcmp(cdr->bridge, cand_cdr->bridge)) {
+ cand_cdr_master = ao2_find(active_cdrs_master, channel_id, OBJ_SEARCH_KEY);
+ if (!cand_cdr_master) {
continue;
}
- /* If we don't have a Party B (originated channel), skip it */
- if (!cdr->party_b.snapshot) {
- continue;
- }
+ ao2_lock(cand_cdr_master);
+ for (cand_cdr = cand_cdr_master; cand_cdr; cand_cdr = cand_cdr->next) {
+ /* Skip any records that are not in a bridge or in this bridge.
+ * I'm not sure how that would happen, but it pays to be careful. */
+ if (cand_cdr->fn_table != &bridge_state_fn_table
+ || strcmp(cdr->bridge, cand_cdr->bridge)) {
+ continue;
+ }
- /* Skip any records that aren't our Party B */
- if (strcasecmp(cdr->party_b.snapshot->name, cand_cdr->party_a.snapshot->name)) {
- continue;
- }
- cdr_object_snapshot_copy(&cdr->party_b, &cand_cdr->party_a);
- /* If they have a Party B, they joined up with someone else as their
- * Party A. Don't finalize them as they're active. Otherwise, we
- * have stolen them so they need to be finalized.
- */
- if (!cand_cdr->party_b.snapshot) {
- cdr_object_finalize(cand_cdr);
+ /* Skip any records that aren't our Party B */
+ if (strcasecmp(cdr->party_b.snapshot->name, cand_cdr->party_a.snapshot->name)) {
+ continue;
+ }
+ cdr_object_snapshot_copy(&cdr->party_b, &cand_cdr->party_a);
+ /* If they have a Party B, they joined up with someone else as their
+ * Party A. Don't finalize them as they're active. Otherwise, we
+ * have stolen them so they need to be finalized.
+ */
+ if (!cand_cdr->party_b.snapshot) {
+ cdr_object_finalize(cand_cdr);
+ }
+ success = 1;
+ break;
}
- success = 1;
- break;
+ ao2_unlock(cand_cdr_master);
+ ao2_cleanup(cand_cdr_master);
}
- ao2_unlock(cand_cdr_master);
+ ao2_iterator_destroy(&it_cdrs);
}
- ao2_iterator_destroy(&it_cdrs);
/* We always transition state, even if we didn't get a peer */
cdr_object_transition_state(cdr, &bridge_state_fn_table);
static void bridge_state_process_party_b(struct cdr_object *cdr, struct ast_channel_snapshot *snapshot)
{
- if (!cdr->party_b.snapshot
- || strcasecmp(cdr->party_b.snapshot->name, snapshot->name)) {
- return;
- }
+ ast_assert(cdr->party_b.snapshot
+ && !strcasecmp(cdr->party_b.snapshot->name, snapshot->name));
+
cdr_object_swap_snapshot(&cdr->party_b, snapshot);
/* If party B hangs up, finalize this CDR */
static int finalized_state_process_party_a(struct cdr_object *cdr, struct ast_channel_snapshot *snapshot)
{
- RAII_VAR(struct module_config *, mod_cfg,
- ao2_global_obj_ref(module_configs), ao2_cleanup);
-
if (ast_test_flag(&snapshot->softhangup_flags, AST_SOFTHANGUP_HANGUP_EXEC)
- && ast_test_flag(&mod_cfg->general->settings, CDR_END_BEFORE_H_EXTEN)) {
+ && is_cdr_flag_set(CDR_END_BEFORE_H_EXTEN)) {
return 0;
}
return ret;
}
+static int dial_status_end(const char *dialstatus)
+{
+ return (strcmp(dialstatus, "RINGING") &&
+ strcmp(dialstatus, "PROCEEDING") &&
+ strcmp(dialstatus, "PROGRESS"));
+}
+
/* TOPIC ROUTER CALLBACKS */
/*!
*/
static void handle_dial_message(void *data, struct stasis_subscription *sub, struct stasis_message *message)
{
- RAII_VAR(struct module_config *, mod_cfg, ao2_global_obj_ref(module_configs), ao2_cleanup);
- RAII_VAR(struct cdr_object *, cdr, NULL, ao2_cleanup);
+ struct cdr_object *cdr;
struct ast_multi_channel_blob *payload = stasis_message_data(message);
struct ast_channel_snapshot *caller;
struct ast_channel_snapshot *peer;
if (!peer && !caller) {
return;
}
+
+ if (peer && filter_channel_snapshot(peer)) {
+ return;
+ }
+
+ if (caller && filter_channel_snapshot(caller)) {
+ return;
+ }
+
dial_status_blob = ast_json_object_get(ast_multi_channel_blob_get_json(payload), "dialstatus");
if (dial_status_blob) {
dial_status = ast_json_string_get(dial_status_blob);
}
- CDR_DEBUG(mod_cfg, "Dial %s message for %s, %s: %u.%08u\n",
- ast_strlen_zero(dial_status) ? "Begin" : "End",
- caller ? caller->name : "(none)",
- peer ? peer->name : "(none)",
- (unsigned int)stasis_message_timestamp(message)->tv_sec,
- (unsigned int)stasis_message_timestamp(message)->tv_usec);
-
- if (filter_channel_snapshot(peer) || (caller && filter_channel_snapshot(caller))) {
- return;
- }
+ CDR_DEBUG("Dial %s message for %s, %s: %u.%08u\n",
+ ast_strlen_zero(dial_status) ? "Begin" : "End",
+ caller ? caller->name : "(none)",
+ peer ? peer->name : "(none)",
+ (unsigned int)stasis_message_timestamp(message)->tv_sec,
+ (unsigned int)stasis_message_timestamp(message)->tv_usec);
/* Figure out who is running this show */
if (caller) {
- cdr = ao2_find(active_cdrs_by_channel, caller->uniqueid, OBJ_KEY);
+ cdr = ao2_find(active_cdrs_master, caller->uniqueid, OBJ_SEARCH_KEY);
} else {
- cdr = ao2_find(active_cdrs_by_channel, peer->uniqueid, OBJ_KEY);
+ cdr = ao2_find(active_cdrs_master, peer->uniqueid, OBJ_SEARCH_KEY);
}
-
if (!cdr) {
ast_log(AST_LOG_WARNING, "No CDR for channel %s\n", caller ? caller->name : peer->name);
ast_assert(0);
if (!it_cdr->fn_table->process_dial_begin) {
continue;
}
- CDR_DEBUG(mod_cfg, "%p - Processing Dial Begin message for channel %s, peer %s\n",
- it_cdr,
- caller ? caller->name : "(none)",
- peer ? peer->name : "(none)");
+ CDR_DEBUG("%p - Processing Dial Begin message for channel %s, peer %s\n",
+ it_cdr,
+ caller ? caller->name : "(none)",
+ peer ? peer->name : "(none)");
res &= it_cdr->fn_table->process_dial_begin(it_cdr,
caller,
peer);
- } else {
+ } else if (dial_status_end(dial_status)) {
if (!it_cdr->fn_table->process_dial_end) {
continue;
}
- CDR_DEBUG(mod_cfg, "%p - Processing Dial End message for channel %s, peer %s\n",
- it_cdr,
- caller ? caller->name : "(none)",
- peer ? peer->name : "(none)");
+ CDR_DEBUG("%p - Processing Dial End message for channel %s, peer %s\n",
+ it_cdr,
+ caller ? caller->name : "(none)",
+ peer ? peer->name : "(none)");
it_cdr->fn_table->process_dial_end(it_cdr,
caller,
peer,
struct cdr_object *new_cdr;
new_cdr = cdr_object_create_and_append(cdr);
- if (!new_cdr) {
- ao2_unlock(cdr);
- return;
+ if (new_cdr) {
+ new_cdr->fn_table->process_dial_begin(new_cdr, caller, peer);
}
- new_cdr->fn_table->process_dial_begin(new_cdr,
- caller,
- peer);
}
ao2_unlock(cdr);
+ ao2_cleanup(cdr);
}
-static int cdr_object_finalize_party_b(void *obj, void *arg, int flags)
+static int cdr_object_finalize_party_b(void *obj, void *arg, void *data, int flags)
{
struct cdr_object *cdr = obj;
- struct ast_channel_snapshot *party_b = arg;
- struct cdr_object *it_cdr;
- for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->next) {
- if (it_cdr->party_b.snapshot
- && !strcasecmp(it_cdr->party_b.snapshot->name, party_b->name)) {
- /* Don't transition to the finalized state - let the Party A do
- * that when its ready
- */
- cdr_object_finalize(it_cdr);
- }
+
+ if (!strcasecmp(cdr->party_b_name, arg)) {
+#ifdef AST_DEVMODE
+ struct ast_channel_snapshot *party_b = data;
+
+ /*
+ * For sanity's sake we also assert the party_b snapshot
+ * is consistent with the key.
+ */
+ ast_assert(cdr->party_b.snapshot
+ && !strcasecmp(cdr->party_b.snapshot->name, party_b->name));
+#endif
+
+ /* Don't transition to the finalized state - let the Party A do
+ * that when its ready
+ */
+ cdr_object_finalize(cdr);
}
return 0;
}
-static int cdr_object_update_party_b(void *obj, void *arg, int flags)
+static int cdr_object_update_party_b(void *obj, void *arg, void *data, int flags)
{
struct cdr_object *cdr = obj;
- struct ast_channel_snapshot *party_b = arg;
- struct cdr_object *it_cdr;
- for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->next) {
- if (!it_cdr->fn_table->process_party_b) {
- continue;
- }
- if (it_cdr->party_b.snapshot
- && !strcasecmp(it_cdr->party_b.snapshot->name, party_b->name)) {
- it_cdr->fn_table->process_party_b(it_cdr, party_b);
+
+ if (cdr->fn_table->process_party_b
+ && !strcasecmp(cdr->party_b_name, arg)) {
+ struct ast_channel_snapshot *party_b = data;
+
+ /*
+ * For sanity's sake we also check the party_b snapshot
+ * for consistency with the key. The callback needs and
+ * asserts the snapshot to be this way.
+ */
+ if (!cdr->party_b.snapshot
+ || strcasecmp(cdr->party_b.snapshot->name, party_b->name)) {
+ ast_log(LOG_NOTICE,
+ "CDR for Party A %s(%s) has inconsistent Party B %s name. Message can be ignored but this shouldn't happen.\n",
+ cdr->linkedid,
+ cdr->party_a.snapshot->name,
+ cdr->party_b_name);
+ return 0;
}
+
+ cdr->fn_table->process_party_b(cdr, party_b);
}
return 0;
}
static int check_new_cdr_needed(struct ast_channel_snapshot *old_snapshot,
struct ast_channel_snapshot *new_snapshot)
{
- RAII_VAR(struct module_config *, mod_cfg,
- ao2_global_obj_ref(module_configs), ao2_cleanup);
-
/* If we're dead, we don't need a new CDR */
if (!new_snapshot
|| (ast_test_flag(&new_snapshot->softhangup_flags, AST_SOFTHANGUP_HANGUP_EXEC)
- && ast_test_flag(&mod_cfg->general->settings, CDR_END_BEFORE_H_EXTEN))) {
+ && is_cdr_flag_set(CDR_END_BEFORE_H_EXTEN))) {
return 0;
}
*/
static void handle_channel_cache_message(void *data, struct stasis_subscription *sub, struct stasis_message *message)
{
- RAII_VAR(struct cdr_object *, cdr, NULL, ao2_cleanup);
- RAII_VAR(struct module_config *, mod_cfg, ao2_global_obj_ref(module_configs), ao2_cleanup);
+ struct cdr_object *cdr;
struct stasis_cache_update *update = stasis_message_data(message);
struct ast_channel_snapshot *old_snapshot;
struct ast_channel_snapshot *new_snapshot;
- const char *uniqueid;
- const char *name;
struct cdr_object *it_cdr;
ast_assert(update != NULL);
old_snapshot = stasis_message_data(update->old_snapshot);
new_snapshot = stasis_message_data(update->new_snapshot);
- uniqueid = new_snapshot ? new_snapshot->uniqueid : old_snapshot->uniqueid;
- name = new_snapshot ? new_snapshot->name : old_snapshot->name;
if (filter_channel_cache_message(old_snapshot, new_snapshot)) {
return;
if (!cdr) {
return;
}
- ao2_link(active_cdrs_by_channel, cdr);
+ cdr->is_root = 1;
+ ao2_link(active_cdrs_master, cdr);
+ } else {
+ const char *uniqueid;
+
+ uniqueid = new_snapshot ? new_snapshot->uniqueid : old_snapshot->uniqueid;
+ cdr = ao2_find(active_cdrs_master, uniqueid, OBJ_SEARCH_KEY);
}
/* Handle Party A */
if (!cdr) {
- cdr = ao2_find(active_cdrs_by_channel, uniqueid, OBJ_KEY);
- }
- if (!cdr) {
+ const char *name;
+
+ name = new_snapshot ? new_snapshot->name : old_snapshot->name;
ast_log(AST_LOG_WARNING, "No CDR for channel %s\n", name);
ast_assert(0);
- } else {
+ } else if (new_snapshot) {
+ int all_reject = 1;
+
ao2_lock(cdr);
- if (new_snapshot) {
- int all_reject = 1;
- for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->next) {
- if (!it_cdr->fn_table->process_party_a) {
- continue;
- }
- all_reject &= it_cdr->fn_table->process_party_a(it_cdr, new_snapshot);
- }
- if (all_reject && check_new_cdr_needed(old_snapshot, new_snapshot)) {
- /* We're not hung up and we have a new snapshot - we need a new CDR */
- struct cdr_object *new_cdr;
- new_cdr = cdr_object_create_and_append(cdr);
- if (new_cdr) {
- new_cdr->fn_table->process_party_a(new_cdr, new_snapshot);
- }
+ for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->next) {
+ if (!it_cdr->fn_table->process_party_a) {
+ continue;
}
- } else {
- CDR_DEBUG(mod_cfg, "%p - Beginning finalize/dispatch for %s\n", cdr, old_snapshot->name);
- for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->next) {
- cdr_object_finalize(it_cdr);
+ all_reject &= it_cdr->fn_table->process_party_a(it_cdr, new_snapshot);
+ }
+ if (all_reject && check_new_cdr_needed(old_snapshot, new_snapshot)) {
+ /* We're not hung up and we have a new snapshot - we need a new CDR */
+ struct cdr_object *new_cdr;
+
+ new_cdr = cdr_object_create_and_append(cdr);
+ if (new_cdr) {
+ new_cdr->fn_table->process_party_a(new_cdr, new_snapshot);
}
- cdr_object_dispatch(cdr);
- ao2_unlink(active_cdrs_by_channel, cdr);
}
ao2_unlock(cdr);
+ } else {
+ ao2_lock(cdr);
+ CDR_DEBUG("%p - Beginning finalize/dispatch for %s\n", cdr, old_snapshot->name);
+ for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->next) {
+ cdr_object_finalize(it_cdr);
+ }
+ cdr_object_dispatch(cdr);
+ ao2_unlock(cdr);
+
+ cdr_all_unlink(cdr);
+ ao2_unlink(active_cdrs_master, cdr);
}
/* Handle Party B */
if (new_snapshot) {
- ao2_callback(active_cdrs_by_channel, OBJ_NODATA, cdr_object_update_party_b,
- new_snapshot);
+ ao2_callback_data(active_cdrs_all, OBJ_NODATA | OBJ_MULTIPLE | OBJ_SEARCH_KEY,
+ cdr_object_update_party_b, (char *) new_snapshot->name, new_snapshot);
} else {
- ao2_callback(active_cdrs_by_channel, OBJ_NODATA, cdr_object_finalize_party_b,
- old_snapshot);
+ ao2_callback_data(active_cdrs_all, OBJ_NODATA | OBJ_MULTIPLE | OBJ_SEARCH_KEY,
+ cdr_object_finalize_party_b, (char *) old_snapshot->name, old_snapshot);
}
+ ao2_cleanup(cdr);
}
struct bridge_leave_data {
};
/*! \brief Callback used to notify CDRs of a Party B leaving the bridge */
-static int cdr_object_party_b_left_bridge_cb(void *obj, void *arg, int flags)
+static int cdr_object_party_b_left_bridge_cb(void *obj, void *arg, void *data, int flags)
{
struct cdr_object *cdr = obj;
- struct bridge_leave_data *leave_data = arg;
- struct cdr_object *it_cdr;
+ struct bridge_leave_data *leave_data = data;
+
+ if (cdr->fn_table == &bridge_state_fn_table
+ && !strcmp(cdr->bridge, leave_data->bridge->uniqueid)
+ && !strcasecmp(cdr->party_b_name, arg)) {
+ /*
+ * For sanity's sake we also assert the party_b snapshot
+ * is consistent with the key.
+ */
+ ast_assert(cdr->party_b.snapshot
+ && !strcasecmp(cdr->party_b.snapshot->name, leave_data->channel->name));
- if (strcmp(cdr->bridge, leave_data->bridge->uniqueid)) {
- return 0;
- }
- for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->next) {
- if (it_cdr->fn_table != &bridge_state_fn_table) {
- continue;
- }
- if (!it_cdr->party_b.snapshot) {
- continue;
- }
- if (strcasecmp(it_cdr->party_b.snapshot->name, leave_data->channel->name)) {
- continue;
- }
/* It is our Party B, in our bridge. Set the end time and let the handler
* transition our CDR appropriately when we leave the bridge.
*/
- cdr_object_finalize(it_cdr);
+ cdr_object_finalize(cdr);
}
return 0;
}
struct ast_bridge_blob *update = stasis_message_data(message);
struct ast_bridge_snapshot *bridge = update->bridge;
struct ast_channel_snapshot *channel = update->channel;
- RAII_VAR(struct module_config *, mod_cfg,
- ao2_global_obj_ref(module_configs), ao2_cleanup);
- RAII_VAR(struct cdr_object *, cdr,
- ao2_find(active_cdrs_by_channel, channel->uniqueid, OBJ_KEY),
- ao2_cleanup);
+ struct cdr_object *cdr;
struct cdr_object *it_cdr;
struct bridge_leave_data leave_data = {
.bridge = bridge,
return;
}
- CDR_DEBUG(mod_cfg, "Bridge Leave message for %s: %u.%08u\n",
- channel->name,
- (unsigned int)stasis_message_timestamp(message)->tv_sec,
- (unsigned int)stasis_message_timestamp(message)->tv_usec);
+ CDR_DEBUG("Bridge Leave message for %s: %u.%08u\n",
+ channel->name,
+ (unsigned int)stasis_message_timestamp(message)->tv_sec,
+ (unsigned int)stasis_message_timestamp(message)->tv_usec);
+ cdr = ao2_find(active_cdrs_master, channel->uniqueid, OBJ_SEARCH_KEY);
if (!cdr) {
ast_log(AST_LOG_WARNING, "No CDR for channel %s\n", channel->name);
ast_assert(0);
if (!it_cdr->fn_table->process_bridge_leave) {
continue;
}
- CDR_DEBUG(mod_cfg, "%p - Processing Bridge Leave for %s\n",
- it_cdr, channel->name);
+ CDR_DEBUG("%p - Processing Bridge Leave for %s\n",
+ it_cdr, channel->name);
if (!it_cdr->fn_table->process_bridge_leave(it_cdr, bridge, channel)) {
ast_string_field_set(it_cdr, bridge, "");
left_bridge = 1;
}
}
ao2_unlock(cdr);
- if (!left_bridge) {
- return;
- }
- if (strcmp(bridge->subclass, "parking")) {
- /* Party B */
- ao2_callback(active_cdrs_by_channel, OBJ_NODATA,
- cdr_object_party_b_left_bridge_cb,
- &leave_data);
+ /* Party B */
+ if (left_bridge
+ && strcmp(bridge->subclass, "parking")) {
+ ao2_callback_data(active_cdrs_all, OBJ_NODATA | OBJ_MULTIPLE | OBJ_SEARCH_KEY,
+ cdr_object_party_b_left_bridge_cb, (char *) leave_data.channel->name,
+ &leave_data);
}
+
+ ao2_cleanup(cdr);
}
/*!
static void bridge_candidate_add_to_cdr(struct cdr_object *cdr,
struct cdr_object_snapshot *party_b)
{
- RAII_VAR(struct module_config *, mod_cfg,
- ao2_global_obj_ref(module_configs), ao2_cleanup);
struct cdr_object *new_cdr;
new_cdr = cdr_object_create_and_append(cdr);
return;
}
cdr_object_snapshot_copy(&new_cdr->party_b, party_b);
+ cdr_all_relink(new_cdr);
cdr_object_check_party_a_answer(new_cdr);
ast_string_field_set(new_cdr, bridge, cdr->bridge);
cdr_object_transition_state(new_cdr, &bridge_state_fn_table);
- CDR_DEBUG(mod_cfg, "%p - Party A %s has new Party B %s\n",
+ CDR_DEBUG("%p - Party A %s has new Party B %s\n",
new_cdr, new_cdr->party_a.snapshot->name,
party_b->snapshot->name);
}
* \param cand_cdr The \ref cdr_object that is a candidate
*
*/
-static int bridge_candidate_process(struct cdr_object *cdr, struct cdr_object *base_cand_cdr)
+static void bridge_candidate_process(struct cdr_object *cdr, struct cdr_object *base_cand_cdr)
{
- RAII_VAR(struct module_config *, mod_cfg,
- ao2_global_obj_ref(module_configs), ao2_cleanup);
struct cdr_object_snapshot *party_a;
struct cdr_object *cand_cdr;
- SCOPED_AO2LOCK(lock, base_cand_cdr);
+ ao2_lock(base_cand_cdr);
for (cand_cdr = base_cand_cdr; cand_cdr; cand_cdr = cand_cdr->next) {
/* Skip any records that are not in this bridge */
if (!strcasecmp(cdr->party_a.snapshot->name, cand_cdr->party_a.snapshot->name)
|| (cdr->party_b.snapshot
&& !strcasecmp(cdr->party_b.snapshot->name, cand_cdr->party_a.snapshot->name))) {
- return 0;
+ break;
}
party_a = cdr_object_pick_party_a(&cdr->party_a, &cand_cdr->party_a);
* Party B */
if (!strcasecmp(party_a->snapshot->name, cdr->party_a.snapshot->name)) {
bridge_candidate_add_to_cdr(cdr, &cand_cdr->party_a);
- return 0;
+ break;
}
/* We're Party B. Check if we can add ourselves immediately or if we need
&& strcasecmp(cand_cdr->party_b.snapshot->name, cdr->party_a.snapshot->name)) {
bridge_candidate_add_to_cdr(cand_cdr, &cdr->party_a);
} else {
- CDR_DEBUG(mod_cfg, "%p - Party A %s has new Party B %s\n",
+ CDR_DEBUG("%p - Party A %s has new Party B %s\n",
cand_cdr, cand_cdr->party_a.snapshot->name,
cdr->party_a.snapshot->name);
cdr_object_snapshot_copy(&cand_cdr->party_b, &cdr->party_a);
+ cdr_all_relink(cand_cdr);
/* It's possible that this joined at one point and was never chosen
* as party A. Clear their end time, as it would be set in such a
* case.
*/
memset(&cand_cdr->end, 0, sizeof(cand_cdr->end));
}
- return 0;
+
+ break;
}
- return 0;
+
+ ao2_unlock(base_cand_cdr);
}
/*!
it_channels = ao2_iterator_init(bridge->channels, 0);
while ((channel_id = ao2_iterator_next(&it_channels))) {
- RAII_VAR(struct cdr_object *, cand_cdr,
- ao2_find(active_cdrs_by_channel, channel_id, OBJ_KEY),
- ao2_cleanup);
+ struct cdr_object *cand_cdr;
- if (!cand_cdr) {
- ao2_ref(channel_id, -1);
- continue;
+ cand_cdr = ao2_find(active_cdrs_master, channel_id, OBJ_SEARCH_KEY);
+ if (cand_cdr) {
+ bridge_candidate_process(cdr, cand_cdr);
+ ao2_ref(cand_cdr, -1);
}
- bridge_candidate_process(cdr, cand_cdr);
-
ao2_ref(channel_id, -1);
}
ao2_iterator_destroy(&it_channels);
struct ast_bridge_snapshot *bridge,
struct ast_channel_snapshot *channel)
{
- RAII_VAR(struct module_config *, mod_cfg,
- ao2_global_obj_ref(module_configs), ao2_cleanup);
int res = 1;
struct cdr_object *it_cdr;
struct cdr_object *new_cdr;
res &= it_cdr->fn_table->process_parking_bridge_enter(it_cdr, bridge, channel);
}
if (it_cdr->fn_table->process_party_a) {
- CDR_DEBUG(mod_cfg, "%p - Updating Party A %s snapshot\n", it_cdr,
- channel->name);
+ CDR_DEBUG("%p - Updating Party A %s snapshot\n", it_cdr,
+ channel->name);
it_cdr->fn_table->process_party_a(it_cdr, channel);
}
}
struct ast_bridge_snapshot *bridge,
struct ast_channel_snapshot *channel)
{
- RAII_VAR(struct module_config *, mod_cfg,
- ao2_global_obj_ref(module_configs), ao2_cleanup);
enum process_bridge_enter_results result;
struct cdr_object *it_cdr;
struct cdr_object *new_cdr;
ao2_lock(cdr);
+try_again:
for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->next) {
if (it_cdr->fn_table->process_party_a) {
- CDR_DEBUG(mod_cfg, "%p - Updating Party A %s snapshot\n", it_cdr,
- channel->name);
+ CDR_DEBUG("%p - Updating Party A %s snapshot\n", it_cdr,
+ channel->name);
it_cdr->fn_table->process_party_a(it_cdr, channel);
}
/* Notify all states that they have entered a bridge */
if (it_cdr->fn_table->process_bridge_enter) {
- CDR_DEBUG(mod_cfg, "%p - Processing bridge enter for %s\n", it_cdr,
- channel->name);
+ CDR_DEBUG("%p - Processing bridge enter for %s\n", it_cdr,
+ channel->name);
result = it_cdr->fn_table->process_bridge_enter(it_cdr, bridge, channel);
switch (result) {
case BRIDGE_ENTER_ONLY_PARTY:
/* This is guaranteed to succeed: the new CDR is created in the single state
* and will be able to handle the bridge enter message
*/
- handle_standard_bridge_enter_message(cdr, bridge, channel);
+ goto try_again;
}
}
ao2_unlock(cdr);
struct ast_bridge_blob *update = stasis_message_data(message);
struct ast_bridge_snapshot *bridge = update->bridge;
struct ast_channel_snapshot *channel = update->channel;
- RAII_VAR(struct cdr_object *, cdr,
- ao2_find(active_cdrs_by_channel, channel->uniqueid, OBJ_KEY),
- ao2_cleanup);
- RAII_VAR(struct module_config *, mod_cfg,
- ao2_global_obj_ref(module_configs), ao2_cleanup);
+ struct cdr_object *cdr;
if (filter_bridge_messages(bridge)) {
return;
return;
}
- CDR_DEBUG(mod_cfg, "Bridge Enter message for channel %s: %u.%08u\n",
- channel->name,
- (unsigned int)stasis_message_timestamp(message)->tv_sec,
- (unsigned int)stasis_message_timestamp(message)->tv_usec);
+ CDR_DEBUG("Bridge Enter message for channel %s: %u.%08u\n",
+ channel->name,
+ (unsigned int)stasis_message_timestamp(message)->tv_sec,
+ (unsigned int)stasis_message_timestamp(message)->tv_usec);
+ cdr = ao2_find(active_cdrs_master, channel->uniqueid, OBJ_SEARCH_KEY);
if (!cdr) {
ast_log(AST_LOG_WARNING, "No CDR for channel %s\n", channel->name);
ast_assert(0);
} else {
handle_standard_bridge_enter_message(cdr, bridge, channel);
}
+ ao2_cleanup(cdr);
}
/*!
{
struct ast_parked_call_payload *payload = stasis_message_data(message);
struct ast_channel_snapshot *channel = payload->parkee;
- RAII_VAR(struct cdr_object *, cdr, NULL, ao2_cleanup);
- RAII_VAR(struct module_config *, mod_cfg,
- ao2_global_obj_ref(module_configs), ao2_cleanup);
+ struct cdr_object *cdr;
int unhandled = 1;
struct cdr_object *it_cdr;
return;
}
- CDR_DEBUG(mod_cfg, "Parked Call message for channel %s: %u.%08u\n",
- channel->name,
- (unsigned int)stasis_message_timestamp(message)->tv_sec,
- (unsigned int)stasis_message_timestamp(message)->tv_usec);
+ CDR_DEBUG("Parked Call message for channel %s: %u.%08u\n",
+ channel->name,
+ (unsigned int)stasis_message_timestamp(message)->tv_sec,
+ (unsigned int)stasis_message_timestamp(message)->tv_usec);
- cdr = ao2_find(active_cdrs_by_channel, channel->uniqueid, OBJ_KEY);
+ cdr = ao2_find(active_cdrs_master, channel->uniqueid, OBJ_SEARCH_KEY);
if (!cdr) {
ast_log(AST_LOG_WARNING, "No CDR for channel %s\n", channel->name);
ast_assert(0);
if (unhandled) {
/* Nothing handled the messgae - we need a new one! */
- struct cdr_object *new_cdr = cdr_object_create_and_append(cdr);
+ struct cdr_object *new_cdr;
+
+ new_cdr = cdr_object_create_and_append(cdr);
if (new_cdr) {
/* As the new CDR is created in the single state, it is guaranteed
* to have a function for the parked call message and will handle
ao2_unlock(cdr);
+ ao2_cleanup(cdr);
}
/*!
struct ast_cdr_config *ast_cdr_get_config(void)
{
- RAII_VAR(struct module_config *, mod_cfg, ao2_global_obj_ref(module_configs), ao2_cleanup);
- ao2_ref(mod_cfg->general, +1);
- return mod_cfg->general;
+ struct ast_cdr_config *general;
+ struct module_config *mod_cfg;
+
+ mod_cfg = ao2_global_obj_ref(module_configs);
+ if (!mod_cfg) {
+ return NULL;
+ }
+ general = ao2_bump(mod_cfg->general);
+ ao2_cleanup(mod_cfg);
+ return general;
}
void ast_cdr_set_config(struct ast_cdr_config *config)
{
- RAII_VAR(struct module_config *, mod_cfg, ao2_global_obj_ref(module_configs), ao2_cleanup);
+ struct module_config *mod_cfg;
+
+ if (!config) {
+ return;
+ }
- ao2_cleanup(mod_cfg->general);
- mod_cfg->general = config;
- ao2_ref(mod_cfg->general, +1);
+ mod_cfg = ao2_global_obj_ref(module_configs);
+ if (!mod_cfg) {
+ return;
+ }
+ ao2_replace(mod_cfg->general, config);
+
+ cdr_set_debug_mode(mod_cfg);
cdr_toggle_runtime_options();
+
+ ao2_cleanup(mod_cfg);
}
int ast_cdr_is_enabled(void)
{
- RAII_VAR(struct module_config *, mod_cfg, ao2_global_obj_ref(module_configs), ao2_cleanup);
- return ast_test_flag(&mod_cfg->general->settings, CDR_ENABLED);
+ return is_cdr_flag_set(CDR_ENABLED);
}
int ast_cdr_backend_suspend(const char *name)
return success;
}
-int ast_cdr_register(const char *name, const char *desc, ast_cdrbe be)
+static int cdr_generic_register(struct be_list *generic_list, const char *name, const char *desc, ast_cdrbe be)
{
- struct cdr_beitem *i = NULL;
+ struct cdr_beitem *i;
+ struct cdr_beitem *cur;
- if (!name)
+ if (!name) {
return -1;
+ }
if (!be) {
ast_log(LOG_WARNING, "CDR engine '%s' lacks backend\n", name);
- return -1;
- }
- AST_RWLIST_WRLOCK(&be_list);
- AST_RWLIST_TRAVERSE(&be_list, i, list) {
- if (!strcasecmp(name, i->name)) {
- ast_log(LOG_WARNING, "Already have a CDR backend called '%s'\n", name);
- AST_RWLIST_UNLOCK(&be_list);
- return -1;
- }
+ return -1;
}
- if (!(i = ast_calloc(1, sizeof(*i))))
+ i = ast_calloc(1, sizeof(*i));
+ if (!i) {
return -1;
+ }
i->be = be;
ast_copy_string(i->name, name, sizeof(i->name));
ast_copy_string(i->desc, desc, sizeof(i->desc));
- AST_RWLIST_INSERT_HEAD(&be_list, i, list);
- AST_RWLIST_UNLOCK(&be_list);
+ AST_RWLIST_WRLOCK(generic_list);
+ AST_RWLIST_TRAVERSE(generic_list, cur, list) {
+ if (!strcasecmp(name, cur->name)) {
+ ast_log(LOG_WARNING, "Already have a CDR backend called '%s'\n", name);
+ AST_RWLIST_UNLOCK(generic_list);
+ ast_free(i);
+
+ return -1;
+ }
+ }
+
+ AST_RWLIST_INSERT_HEAD(generic_list, i, list);
+ AST_RWLIST_UNLOCK(generic_list);
return 0;
}
-int ast_cdr_unregister(const char *name)
+int ast_cdr_register(const char *name, const char *desc, ast_cdrbe be)
+{
+ return cdr_generic_register(&be_list, name, desc, be);
+}
+
+int ast_cdr_modifier_register(const char *name, const char *desc, ast_cdrbe be)
+{
+ return cdr_generic_register((struct be_list *)&mo_list, name, desc, be);
+}
+
+static int ast_cdr_generic_unregister(struct be_list *generic_list, const char *name)
{
struct cdr_beitem *match = NULL;
int active_count;
- AST_RWLIST_WRLOCK(&be_list);
- AST_RWLIST_TRAVERSE(&be_list, match, list) {
+ AST_RWLIST_WRLOCK(generic_list);
+ AST_RWLIST_TRAVERSE(generic_list, match, list) {
if (!strcasecmp(name, match->name)) {
break;
}
}
if (!match) {
- AST_RWLIST_UNLOCK(&be_list);
+ AST_RWLIST_UNLOCK(generic_list);
return 0;
}
- active_count = ao2_container_count(active_cdrs_by_channel);
+ active_count = ao2_container_count(active_cdrs_master);
if (!match->suspended && active_count != 0) {
- AST_RWLIST_UNLOCK(&be_list);
+ AST_RWLIST_UNLOCK(generic_list);
ast_log(AST_LOG_WARNING, "Unable to unregister CDR backend %s; %d CDRs are still active\n",
name, active_count);
return -1;
}
- AST_RWLIST_REMOVE(&be_list, match, list);
- AST_RWLIST_UNLOCK(&be_list);
+ AST_RWLIST_REMOVE(generic_list, match, list);
+ AST_RWLIST_UNLOCK(generic_list);
ast_verb(2, "Unregistered '%s' CDR backend\n", name);
ast_free(match);
return 0;
}
+int ast_cdr_unregister(const char *name)
+{
+ return ast_cdr_generic_unregister(&be_list, name);
+}
+
+int ast_cdr_modifier_unregister(const char *name)
+{
+ return ast_cdr_generic_unregister((struct be_list *)&mo_list, name);
+}
+
struct ast_cdr *ast_cdr_dup(struct ast_cdr *cdr)
{
struct ast_cdr *newcdr;
}
}
-/*
+/*!
* \internal
* \brief Callback that finds all CDRs that reference a particular channel by name
*/
return 0;
}
-/*
+/*!
* \internal
* \brief Callback that finds a CDR by channel name
*/
}
}
- it_cdrs = ao2_callback(active_cdrs_by_channel, OBJ_MULTIPLE, cdr_object_select_all_by_name_cb, arg);
+ it_cdrs = ao2_callback(active_cdrs_master, OBJ_MULTIPLE, cdr_object_select_all_by_name_cb, arg);
if (!it_cdrs) {
ast_log(AST_LOG_ERROR, "Unable to find CDR for channel %s\n", channel_name);
return -1;
for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->next) {
struct varshead *headp = NULL;
- if (it_cdr->fn_table == &finalized_state_fn_table) {
+ if (it_cdr->fn_table == &finalized_state_fn_table && it_cdr->next != NULL) {
continue;
}
if (!strcasecmp(channel_name, it_cdr->party_a.snapshot->name)) {
}
param = ast_strdupa(name);
- return ao2_callback(active_cdrs_by_channel, 0, cdr_object_get_by_name_cb, param);
+ return ao2_callback(active_cdrs_master, 0, cdr_object_get_by_name_cb, param);
}
int ast_cdr_getvar(const char *channel_name, const char *name, char *value, size_t length)
{
- RAII_VAR(struct cdr_object *, cdr, cdr_object_get_by_name(channel_name), ao2_cleanup);
+ struct cdr_object *cdr;
struct cdr_object *cdr_obj;
- if (!cdr) {
- ast_log(AST_LOG_ERROR, "Unable to find CDR for channel %s\n", channel_name);
+ if (ast_strlen_zero(name)) {
return 1;
}
- if (ast_strlen_zero(name)) {
+ cdr = cdr_object_get_by_name(channel_name);
+ if (!cdr) {
+ ast_log(AST_LOG_ERROR, "Unable to find CDR for channel %s\n", channel_name);
return 1;
}
ao2_unlock(cdr);
+ ao2_cleanup(cdr);
return 0;
}
int ast_cdr_serialize_variables(const char *channel_name, struct ast_str **buf, char delim, char sep)
{
- RAII_VAR(struct cdr_object *, cdr, cdr_object_get_by_name(channel_name), ao2_cleanup);
+ struct cdr_object *cdr;
struct cdr_object *it_cdr;
struct ast_var_t *variable;
const char *var;
char workspace[256];
int total = 0, x = 0, i;
+ cdr = cdr_object_get_by_name(channel_name);
if (!cdr) {
- RAII_VAR(struct module_config *, mod_cfg,
- ao2_global_obj_ref(module_configs), ao2_cleanup);
-
- if (ast_test_flag(&mod_cfg->general->settings, CDR_ENABLED)) {
+ if (is_cdr_flag_set(CDR_ENABLED)) {
ast_log(AST_LOG_ERROR, "Unable to find CDR for channel %s\n", channel_name);
}
-
return 0;
}
ao2_lock(cdr);
for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->next) {
- if (++x > 1)
+ if (++x > 1) {
ast_str_append(buf, 0, "\n");
+ }
AST_LIST_TRAVERSE(&it_cdr->party_a.variables, variable, entries) {
if (!(var = ast_var_name(variable))) {
}
}
ao2_unlock(cdr);
+ ao2_cleanup(cdr);
return total;
}
};
/*! \brief Callback used to update the userfield on Party B on all CDRs */
-static int cdr_object_update_party_b_userfield_cb(void *obj, void *arg, int flags)
+static int cdr_object_update_party_b_userfield_cb(void *obj, void *arg, void *data, int flags)
{
struct cdr_object *cdr = obj;
- struct party_b_userfield_update *info = arg;
- struct cdr_object *it_cdr;
- for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->next) {
- if (it_cdr->fn_table == &finalized_state_fn_table) {
- continue;
- }
- if (it_cdr->party_b.snapshot
- && !strcasecmp(it_cdr->party_b.snapshot->name, info->channel_name)) {
- strcpy(it_cdr->party_b.userfield, info->userfield);
- }
+
+ if ((cdr->fn_table != &finalized_state_fn_table || !cdr->next)
+ && !strcasecmp(cdr->party_b_name, arg)) {
+ struct party_b_userfield_update *info = data;
+
+ /*
+ * For sanity's sake we also assert the party_b snapshot
+ * is consistent with the key.
+ */
+ ast_assert(cdr->party_b.snapshot
+ && !strcasecmp(cdr->party_b.snapshot->name, info->channel_name));
+
+ ast_copy_string(cdr->party_b.userfield, info->userfield,
+ sizeof(cdr->party_b.userfield));
}
+
return 0;
}
void ast_cdr_setuserfield(const char *channel_name, const char *userfield)
{
- RAII_VAR(struct cdr_object *, cdr, cdr_object_get_by_name(channel_name), ao2_cleanup);
+ struct cdr_object *cdr;
struct party_b_userfield_update party_b_info = {
- .channel_name = channel_name,
- .userfield = userfield,
+ .channel_name = channel_name,
+ .userfield = userfield,
};
struct cdr_object *it_cdr;
/* Handle Party A */
+ cdr = cdr_object_get_by_name(channel_name);
if (cdr) {
ao2_lock(cdr);
for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->next) {
- if (it_cdr->fn_table == &finalized_state_fn_table) {
+ if (it_cdr->fn_table == &finalized_state_fn_table && it_cdr->next != NULL) {
continue;
}
- strcpy(it_cdr->party_a.userfield, userfield);
+ ast_copy_string(it_cdr->party_a.userfield, userfield,
+ sizeof(it_cdr->party_a.userfield));
}
ao2_unlock(cdr);
}
/* Handle Party B */
- ao2_callback(active_cdrs_by_channel, OBJ_NODATA,
- cdr_object_update_party_b_userfield_cb,
- &party_b_info);
+ ao2_callback_data(active_cdrs_all, OBJ_NODATA | OBJ_MULTIPLE | OBJ_SEARCH_KEY,
+ cdr_object_update_party_b_userfield_cb, (char *) party_b_info.channel_name,
+ &party_b_info);
+ ao2_cleanup(cdr);
}
static void post_cdr(struct ast_cdr *cdr)
{
- RAII_VAR(struct module_config *, mod_cfg, ao2_global_obj_ref(module_configs), ao2_cleanup);
+ struct module_config *mod_cfg;
struct cdr_beitem *i;
+ mod_cfg = ao2_global_obj_ref(module_configs);
+ if (!mod_cfg) {
+ return;
+ }
+
for (; cdr ; cdr = cdr->next) {
/* For people, who don't want to see unanswered single-channel events */
if (!ast_test_flag(&mod_cfg->general->settings, CDR_UNANSWERED) &&
cdr->disposition < AST_CDR_ANSWERED &&
(ast_strlen_zero(cdr->channel) || ast_strlen_zero(cdr->dstchannel))) {
- ast_debug(1, "Skipping CDR for %s since we weren't answered\n", cdr->channel);
+ ast_debug(1, "Skipping CDR for %s since we weren't answered\n", cdr->channel);
continue;
}
+ /* Modify CDR's */
+ AST_RWLIST_RDLOCK(&mo_list);
+ AST_RWLIST_TRAVERSE(&mo_list, i, list) {
+ i->be(cdr);
+ }
+ AST_RWLIST_UNLOCK(&mo_list);
+
if (ast_test_flag(cdr, AST_CDR_FLAG_DISABLE)) {
continue;
}
}
AST_RWLIST_UNLOCK(&be_list);
}
+ ao2_cleanup(mod_cfg);
}
int ast_cdr_set_property(const char *channel_name, enum ast_cdr_options option)
{
- RAII_VAR(struct cdr_object *, cdr, cdr_object_get_by_name(channel_name), ao2_cleanup);
+ struct cdr_object *cdr;
struct cdr_object *it_cdr;
+ cdr = cdr_object_get_by_name(channel_name);
if (!cdr) {
return -1;
}
}
ao2_unlock(cdr);
+ ao2_cleanup(cdr);
return 0;
}
int ast_cdr_clear_property(const char *channel_name, enum ast_cdr_options option)
{
- RAII_VAR(struct cdr_object *, cdr, cdr_object_get_by_name(channel_name), ao2_cleanup);
+ struct cdr_object *cdr;
struct cdr_object *it_cdr;
+ cdr = cdr_object_get_by_name(channel_name);
if (!cdr) {
return -1;
}
}
ao2_unlock(cdr);
+ ao2_cleanup(cdr);
return 0;
}
int ast_cdr_reset(const char *channel_name, int keep_variables)
{
- RAII_VAR(struct cdr_object *, cdr, cdr_object_get_by_name(channel_name), ao2_cleanup);
+ struct cdr_object *cdr;
struct ast_var_t *vardata;
struct cdr_object *it_cdr;
+ cdr = cdr_object_get_by_name(channel_name);
if (!cdr) {
return -1;
}
}
ao2_unlock(cdr);
+ ao2_cleanup(cdr);
return 0;
}
if (cdr_obj->party_b.snapshot) {
new_cdr->party_b.snapshot = cdr_obj->party_b.snapshot;
ao2_ref(new_cdr->party_b.snapshot, +1);
+ cdr_all_relink(new_cdr);
strcpy(new_cdr->party_b.userfield, cdr_obj->party_b.userfield);
new_cdr->party_b.flags = cdr_obj->party_b.flags;
if (ast_test_flag(options, AST_CDR_FLAG_KEEP_VARS)) {
static void cdr_submit_batch(int do_shutdown)
{
- RAII_VAR(struct module_config *, mod_cfg, ao2_global_obj_ref(module_configs), ao2_cleanup);
+ struct module_config *mod_cfg;
struct cdr_batch_item *oldbatchitems = NULL;
pthread_t batch_post_thread = AST_PTHREADT_NULL;
reset_batch();
ast_mutex_unlock(&cdr_batch_lock);
+ mod_cfg = ao2_global_obj_ref(module_configs);
+
/* if configured, spawn a new thread to post these CDRs,
also try to save as much as possible if we are shutting down safely */
- if (ast_test_flag(&mod_cfg->general->batch_settings.settings, BATCH_MODE_SCHEDULER_ONLY) || do_shutdown) {
+ if (!mod_cfg
+ || ast_test_flag(&mod_cfg->general->batch_settings.settings, BATCH_MODE_SCHEDULER_ONLY)
+ || do_shutdown) {
ast_debug(1, "CDR single-threaded batch processing begins now\n");
do_batch_backend_process(oldbatchitems);
} else {
ast_debug(1, "CDR multi-threaded batch processing begins now\n");
}
}
+
+ ao2_cleanup(mod_cfg);
}
static int submit_scheduled_batch(const void *data)
{
- RAII_VAR(struct module_config *, mod_cfg, ao2_global_obj_ref(module_configs), ao2_cleanup);
+ struct module_config *mod_cfg;
+ int nextms;
+
cdr_submit_batch(0);
- /* manually reschedule from this point in time */
- ast_mutex_lock(&cdr_sched_lock);
- cdr_sched = ast_sched_add(sched, mod_cfg->general->batch_settings.time * 1000, submit_scheduled_batch, NULL);
- ast_mutex_unlock(&cdr_sched_lock);
- /* returning zero so the scheduler does not automatically reschedule */
- return 0;
+ mod_cfg = ao2_global_obj_ref(module_configs);
+ if (!mod_cfg) {
+ return 0;
+ }
+
+ /* Calculate the next scheduled interval */
+ nextms = mod_cfg->general->batch_settings.time * 1000;
+
+ ao2_cleanup(mod_cfg);
+
+ return nextms;
}
/*! Do not hold the batch lock while calling this function */
-static void submit_unscheduled_batch(void)
+static void start_batch_mode(void)
{
/* Prevent two deletes from happening at the same time */
ast_mutex_lock(&cdr_sched_lock);
/* this is okay since we are not being called from within the scheduler */
AST_SCHED_DEL(sched, cdr_sched);
/* schedule the submission to occur ASAP (1 ms) */
- cdr_sched = ast_sched_add(sched, 1, submit_scheduled_batch, NULL);
+ cdr_sched = ast_sched_add_variable(sched, 1, submit_scheduled_batch, NULL, 1);
ast_mutex_unlock(&cdr_sched_lock);
/* signal the do_cdr thread to wakeup early and do some work (that lazy thread ;) */
}
/* maybe they disabled CDR stuff completely, so just drop it */
- if (!ast_test_flag(&mod_cfg->general->settings, CDR_ENABLED)) {
+ if (!mod_cfg || !ast_test_flag(&mod_cfg->general->settings, CDR_ENABLED)) {
ast_debug(1, "Dropping CDR !\n");
ast_cdr_free(cdr);
return;
}
ast_mutex_unlock(&cdr_batch_lock);
- /* Don't call submit_unscheduled_batch with the cdr_batch_lock held */
+ /* Don't submit a batch with cdr_batch_lock held */
if (submit_batch) {
- submit_unscheduled_batch();
+ start_batch_mode();
}
}
struct timeval now;
schedms = ast_sched_wait(sched);
/* this shouldn't happen, but provide a 1 second default just in case */
- if (schedms <= 0)
+ if (schedms < 0)
schedms = 1000;
now = ast_tvadd(ast_tvnow(), ast_samp2tv(schedms, 1000));
timeout.tv_sec = now.tv_sec;
ast_cond_timedwait(&cdr_pending_cond, &cdr_pending_lock, &timeout);
numevents = ast_sched_runq(sched);
ast_mutex_unlock(&cdr_pending_lock);
- ast_debug(2, "Processed %d scheduled CDR batches from the run queue\n", numevents);
+ ast_debug(2, "Processed %d CDR batches from the run queue\n", numevents);
}
return NULL;
static char *handle_cli_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
- RAII_VAR(struct module_config *, mod_cfg, ao2_global_obj_ref(module_configs), ao2_cleanup);
+ struct module_config *mod_cfg;
switch (cmd) {
case CLI_INIT:
return CLI_SHOWUSAGE;
}
+ mod_cfg = ao2_global_obj_ref(module_configs);
+ if (!mod_cfg) {
+ ast_cli(a->fd, "Could not set CDR debugging mode\n");
+ return CLI_SUCCESS;
+ }
if (!strcasecmp(a->argv[3], "on")
&& !ast_test_flag(&mod_cfg->general->settings, CDR_DEBUG)) {
ast_set_flag(&mod_cfg->general->settings, CDR_DEBUG);
ast_clear_flag(&mod_cfg->general->settings, CDR_DEBUG);
ast_cli(a->fd, "CDR debugging disabled\n");
}
+ cdr_set_debug_mode(mod_cfg);
+ ao2_cleanup(mod_cfg);
return CLI_SUCCESS;
}
/*! \brief Complete user input for 'cdr show' */
static char *cli_complete_show(struct ast_cli_args *a)
{
- char *result = NULL;
int wordlen = strlen(a->word);
- int which = 0;
struct ao2_iterator it_cdrs;
struct cdr_object *cdr;
- it_cdrs = ao2_iterator_init(active_cdrs_by_channel, 0);
+ it_cdrs = ao2_iterator_init(active_cdrs_master, 0);
while ((cdr = ao2_iterator_next(&it_cdrs))) {
- if (!strncasecmp(a->word, cdr->party_a.snapshot->name, wordlen) &&
- (++which > a->n)) {
- result = ast_strdup(cdr->party_a.snapshot->name);
- if (result) {
+ if (!strncasecmp(a->word, cdr->party_a.snapshot->name, wordlen)) {
+ if (ast_cli_completion_add(ast_strdup(cdr->party_a.snapshot->name))) {
ao2_ref(cdr, -1);
break;
}
ao2_ref(cdr, -1);
}
ao2_iterator_destroy(&it_cdrs);
- return result;
+
+ return NULL;
}
static void cli_show_channels(struct ast_cli_args *a)
ast_cli(a->fd, "--------------------------------------------------\n");
ast_cli(a->fd, TITLE_STRING, "Channel", "Dst. Channel", "LastApp", "Start", "Answer", "End", "Billsec", "Duration");
- it_cdrs = ao2_iterator_init(active_cdrs_by_channel, 0);
+ it_cdrs = ao2_iterator_init(active_cdrs_master, 0);
for (; (cdr = ao2_iterator_next(&it_cdrs)); ao2_cleanup(cdr)) {
struct cdr_object *it_cdr;
struct timeval start_time = { 0, };
char answer_time_buffer[64];
char end_time_buffer[64];
const char *channel_name = a->argv[3];
- RAII_VAR(struct cdr_object *, cdr, NULL, ao2_cleanup);
+ struct cdr_object *cdr;
#define TITLE_STRING "%-10.10s %-20.20s %-25.25s %-15.15s %-15.15s %-8.8s %-8.8s %-8.8s %-8.8s %-8.8s\n"
#define FORMAT_STRING "%-10.10s %-20.20s %-25.25s %-15.15s %-15.15s %-8.8s %-8.8s %-8.8s %-8.8ld %-8.8ld\n"
ao2_lock(cdr);
for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->next) {
struct timeval end;
+
if (snapshot_is_dialed(it_cdr->party_a.snapshot)) {
continue;
}
(long)ast_tvdiff_ms(end, it_cdr->start) / 1000);
}
ao2_unlock(cdr);
+
+ ao2_cleanup(cdr);
+
#undef FORMAT_STRING
#undef TITLE_STRING
}
static char *handle_cli_status(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
struct cdr_beitem *beitem = NULL;
- RAII_VAR(struct module_config *, mod_cfg, ao2_global_obj_ref(module_configs), ao2_cleanup);
+ struct module_config *mod_cfg;
int cnt = 0;
long nextbatchtime = 0;
return CLI_SHOWUSAGE;
}
+ mod_cfg = ao2_global_obj_ref(module_configs);
+ if (!mod_cfg) {
+ return CLI_FAILURE;
+ }
+
ast_cli(a->fd, "\n");
ast_cli(a->fd, "Call Detail Record (CDR) settings\n");
ast_cli(a->fd, "----------------------------------\n");
ast_cli(a->fd, "\n");
}
+ ao2_cleanup(mod_cfg);
return CLI_SUCCESS;
}
static char *handle_cli_submit(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
- RAII_VAR(struct module_config *, mod_cfg, ao2_global_obj_ref(module_configs), ao2_cleanup);
+ struct module_config *mod_cfg;
switch (cmd) {
case CLI_INIT:
return CLI_SHOWUSAGE;
}
- if (!ast_test_flag(&mod_cfg->general->settings, CDR_ENABLED)) {
- ast_cli(a->fd, "Cannot submit CDR batch: CDR engine disabled.\n");
- return CLI_SUCCESS;
+ mod_cfg = ao2_global_obj_ref(module_configs);
+ if (!mod_cfg) {
+ return CLI_FAILURE;
}
- if (ast_test_flag(&mod_cfg->general->settings, CDR_BATCHMODE)) {
+ if (!ast_test_flag(&mod_cfg->general->settings, CDR_ENABLED)) {
+ ast_cli(a->fd, "Cannot submit CDR batch: CDR engine disabled.\n");
+ } else if (!ast_test_flag(&mod_cfg->general->settings, CDR_BATCHMODE)) {
ast_cli(a->fd, "Cannot submit CDR batch: batch mode not enabled.\n");
- return CLI_SUCCESS;
+ } else {
+ start_batch_mode();
+ ast_cli(a->fd, "Submitted CDRs to backend engines for processing. This may take a while.\n");
}
-
- submit_unscheduled_batch();
- ast_cli(a->fd, "Submitted CDRs to backend engines for processing. This may take a while.\n");
+ ao2_cleanup(mod_cfg);
return CLI_SUCCESS;
}
cdr_object_dispatch(cdr);
ao2_unlock(cdr);
- return 0;
+ cdr_all_unlink(cdr);
+
+ return CMP_MATCH;
}
static void finalize_batch_mode(void)
static int process_config(int reload)
{
- RAII_VAR(struct module_config *, mod_cfg, module_config_alloc(), ao2_cleanup);
-
if (!reload) {
if (aco_info_init(&cfg_info)) {
return 1;
aco_option_register(&cfg_info, "scheduleronly", ACO_EXACT, general_options, DEFAULT_BATCH_SCHEDULER_ONLY, OPT_BOOLFLAG_T, 1, FLDSET(struct ast_cdr_config, batch_settings.settings), BATCH_MODE_SCHEDULER_ONLY);
aco_option_register(&cfg_info, "safeshutdown", ACO_EXACT, general_options, DEFAULT_BATCH_SAFE_SHUTDOWN, OPT_BOOLFLAG_T, 1, FLDSET(struct ast_cdr_config, batch_settings.settings), BATCH_MODE_SAFE_SHUTDOWN);
aco_option_register(&cfg_info, "size", ACO_EXACT, general_options, DEFAULT_BATCH_SIZE, OPT_UINT_T, PARSE_IN_RANGE, FLDSET(struct ast_cdr_config, batch_settings.size), 0, MAX_BATCH_SIZE);
- aco_option_register(&cfg_info, "time", ACO_EXACT, general_options, DEFAULT_BATCH_TIME, OPT_UINT_T, PARSE_IN_RANGE, FLDSET(struct ast_cdr_config, batch_settings.time), 0, MAX_BATCH_TIME);
+ aco_option_register(&cfg_info, "time", ACO_EXACT, general_options, DEFAULT_BATCH_TIME, OPT_UINT_T, PARSE_IN_RANGE, FLDSET(struct ast_cdr_config, batch_settings.time), 1, MAX_BATCH_TIME);
}
- if (aco_process_config(&cfg_info, reload)) {
- if (!mod_cfg) {
+ if (aco_process_config(&cfg_info, reload) == ACO_PROCESS_ERROR) {
+ struct module_config *mod_cfg;
+
+ if (reload) {
return 1;
}
+
/* If we couldn't process the configuration and this wasn't a reload,
* create a default config
*/
- if (!reload && !(aco_set_defaults(&general_option, "general", mod_cfg->general))) {
- ast_log(LOG_NOTICE, "Failed to process CDR configuration; using defaults\n");
- ao2_global_obj_replace_unref(module_configs, mod_cfg);
- return 0;
+ mod_cfg = module_config_alloc();
+ if (!mod_cfg
+ || aco_set_defaults(&general_option, "general", mod_cfg->general)) {
+ ao2_cleanup(mod_cfg);
+ return 1;
}
- return 1;
+ ast_log(LOG_NOTICE, "Failed to process CDR configuration; using defaults\n");
+ ao2_global_obj_replace_unref(module_configs, mod_cfg);
+ cdr_set_debug_mode(mod_cfg);
+ ao2_cleanup(mod_cfg);
}
return 0;
}
-static void cdr_engine_cleanup(void)
-{
- destroy_subscriptions();
-}
-
static void cdr_engine_shutdown(void)
{
stasis_message_router_unsubscribe_and_join(stasis_router);
STASIS_MESSAGE_TYPE_CLEANUP(cdr_sync_message_type);
- ao2_callback(active_cdrs_by_channel, OBJ_NODATA, cdr_object_dispatch_all_cb,
- NULL);
+ ao2_callback(active_cdrs_master, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK,
+ cdr_object_dispatch_all_cb, NULL);
finalize_batch_mode();
ast_cli_unregister_multiple(cli_commands, ARRAY_LEN(cli_commands));
ast_sched_context_destroy(sched);
aco_info_destroy(&cfg_info);
ao2_global_obj_release(module_configs);
- ao2_container_unregister("cdrs_by_channel");
- ao2_ref(active_cdrs_by_channel, -1);
- active_cdrs_by_channel = NULL;
+ ao2_container_unregister("cdrs_master");
+ ao2_cleanup(active_cdrs_master);
+ active_cdrs_master = NULL;
+
+ ao2_container_unregister("cdrs_all");
+ ao2_cleanup(active_cdrs_all);
+ active_cdrs_all = NULL;
}
static void cdr_enable_batch_mode(struct ast_cdr_config *config)
{
- SCOPED_LOCK(batch, &cdr_batch_lock, ast_mutex_lock, ast_mutex_unlock);
-
/* Only create the thread level portions once */
if (cdr_thread == AST_PTHREADT_NULL) {
ast_cond_init(&cdr_pending_cond, NULL);
}
}
- /* Kill the currently scheduled item */
- AST_SCHED_DEL(sched, cdr_sched);
- cdr_sched = ast_sched_add(sched, config->batch_settings.time * 1000, submit_scheduled_batch, NULL);
+ /* Start the batching process */
+ start_batch_mode();
+
ast_log(LOG_NOTICE, "CDR batch mode logging enabled, first of either size %u or time %u seconds.\n",
config->batch_settings.size, config->batch_settings.time);
}
/*!
* \internal
- * \brief Print channel object key (name).
+ * \brief Print master CDR container object.
* \since 12.0.0
*
- * \param v_obj A pointer to the object we want the key printed.
+ * \param v_obj A pointer to the object we want printed.
* \param where User data needed by prnt to determine where to put output.
* \param prnt Print output callback function to use.
*
* \return Nothing
*/
-static void cdr_container_print_fn(void *v_obj, void *where, ao2_prnt_fn *prnt)
+static void cdr_master_print_fn(void *v_obj, void *where, ao2_prnt_fn *prnt)
{
struct cdr_object *cdr = v_obj;
struct cdr_object *it_cdr;
+
if (!cdr) {
return;
}
for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->next) {
- prnt(where, "Party A: %s; Party B: %s; Bridge %s\n", it_cdr->party_a.snapshot->name, it_cdr->party_b.snapshot ? it_cdr->party_b.snapshot->name : "<unknown>",
- it_cdr->bridge);
+ prnt(where, "Party A: %s; Party B: %s; Bridge %s\n",
+ it_cdr->party_a.snapshot->name,
+ it_cdr->party_b.snapshot ? it_cdr->party_b.snapshot->name : "<unknown>",
+ it_cdr->bridge);
+ }
+}
+
+/*!
+ * \internal
+ * \brief Print all CDR container object.
+ * \since 13.19.0
+ *
+ * \param v_obj A pointer to the object we want printed.
+ * \param where User data needed by prnt to determine where to put output.
+ * \param prnt Print output callback function to use.
+ *
+ * \return Nothing
+ */
+static void cdr_all_print_fn(void *v_obj, void *where, ao2_prnt_fn *prnt)
+{
+ struct cdr_object *cdr = v_obj;
+
+ if (!cdr) {
+ return;
}
+ prnt(where, "Party A: %s; Party B: %s; Bridge %s",
+ cdr->party_a.snapshot->name,
+ cdr->party_b.snapshot ? cdr->party_b.snapshot->name : "<unknown>",
+ cdr->bridge);
}
/*!
*/
static int cdr_toggle_runtime_options(void)
{
- RAII_VAR(struct module_config *, mod_cfg,
- ao2_global_obj_ref(module_configs), ao2_cleanup);
+ struct module_config *mod_cfg;
- if (ast_test_flag(&mod_cfg->general->settings, CDR_ENABLED)) {
+ mod_cfg = ao2_global_obj_ref(module_configs);
+ if (mod_cfg
+ && ast_test_flag(&mod_cfg->general->settings, CDR_ENABLED)) {
if (create_subscriptions()) {
destroy_subscriptions();
ast_log(AST_LOG_ERROR, "Failed to create Stasis subscriptions\n");
+ ao2_cleanup(mod_cfg);
return -1;
}
if (ast_test_flag(&mod_cfg->general->settings, CDR_BATCHMODE)) {
destroy_subscriptions();
ast_log(LOG_NOTICE, "CDR logging disabled.\n");
}
+ ao2_cleanup(mod_cfg);
+
+ return mod_cfg ? 0 : -1;
+}
+
+static int unload_module(void)
+{
+ destroy_subscriptions();
return 0;
}
-int ast_cdr_engine_init(void)
+static int load_module(void)
{
if (process_config(0)) {
- return -1;
+ return AST_MODULE_LOAD_FAILURE;
}
cdr_topic = stasis_topic_create("cdr_engine");
if (!cdr_topic) {
- return -1;
+ return AST_MODULE_LOAD_FAILURE;
}
stasis_router = stasis_message_router_create(cdr_topic);
if (!stasis_router) {
- return -1;
+ return AST_MODULE_LOAD_FAILURE;
}
+ stasis_message_router_set_congestion_limits(stasis_router, -1,
+ 10 * AST_TASKPROCESSOR_HIGH_WATER_LEVEL);
if (STASIS_MESSAGE_TYPE_INIT(cdr_sync_message_type)) {
- return -1;
+ return AST_MODULE_LOAD_FAILURE;
}
stasis_message_router_add_cache_update(stasis_router, ast_channel_snapshot_type(), handle_channel_cache_message, NULL);
stasis_message_router_add(stasis_router, ast_parked_call_type(), handle_parked_call_message, NULL);
stasis_message_router_add(stasis_router, cdr_sync_message_type(), handle_cdr_sync_message, NULL);
- active_cdrs_by_channel = ao2_container_alloc(NUM_CDR_BUCKETS,
- cdr_object_channel_hash_fn, cdr_object_channel_cmp_fn);
- if (!active_cdrs_by_channel) {
- return -1;
+ active_cdrs_master = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX, 0,
+ NUM_CDR_BUCKETS, cdr_master_hash_fn, NULL, cdr_master_cmp_fn);
+ if (!active_cdrs_master) {
+ return AST_MODULE_LOAD_FAILURE;
+ }
+ ao2_container_register("cdrs_master", active_cdrs_master, cdr_master_print_fn);
+
+ active_cdrs_all = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX, 0,
+ NUM_CDR_BUCKETS, cdr_all_hash_fn, NULL, cdr_all_cmp_fn);
+ if (!active_cdrs_all) {
+ return AST_MODULE_LOAD_FAILURE;
}
- ao2_container_register("cdrs_by_channel", active_cdrs_by_channel, cdr_container_print_fn);
+ ao2_container_register("cdrs_all", active_cdrs_all, cdr_all_print_fn);
sched = ast_sched_context_create();
if (!sched) {
ast_log(LOG_ERROR, "Unable to create schedule context.\n");
- return -1;
+ return AST_MODULE_LOAD_FAILURE;
}
ast_cli_register_multiple(cli_commands, ARRAY_LEN(cli_commands));
- ast_register_cleanup(cdr_engine_cleanup);
ast_register_atexit(cdr_engine_shutdown);
- return cdr_toggle_runtime_options();
+ return cdr_toggle_runtime_options() ? AST_MODULE_LOAD_FAILURE : AST_MODULE_LOAD_SUCCESS;
}
void ast_cdr_engine_term(void)
{
RAII_VAR(struct module_config *, mod_cfg, ao2_global_obj_ref(module_configs), ao2_cleanup);
- RAII_VAR(void *, payload, NULL, ao2_cleanup);
- RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup);
/* Since this is called explicitly during process shutdown, we might not have ever
* been initialized. If so, the config object will be NULL.
}
if (cdr_sync_message_type()) {
+ void *payload;
+ struct stasis_message *message;
+
+ if (!stasis_router) {
+ return;
+ }
+
/* Make sure we have the needed items */
payload = ao2_alloc(sizeof(*payload), NULL);
- if (!stasis_router || !payload) {
+ if (!payload) {
return;
}
if (message) {
stasis_message_router_publish_sync(stasis_router, message);
}
+ ao2_cleanup(message);
+ ao2_cleanup(payload);
}
if (ast_test_flag(&mod_cfg->general->settings, CDR_BATCHMODE)) {
}
}
-int ast_cdr_engine_reload(void)
+static int reload_module(void)
{
- RAII_VAR(struct module_config *, old_mod_cfg, ao2_global_obj_ref(module_configs), ao2_cleanup);
- RAII_VAR(struct module_config *, mod_cfg, NULL, ao2_cleanup);
+ struct module_config *old_mod_cfg;
+ struct module_config *mod_cfg;
+
+ old_mod_cfg = ao2_global_obj_ref(module_configs);
- if (process_config(1)) {
+ if (!old_mod_cfg || process_config(1)) {
+ ao2_cleanup(old_mod_cfg);
return -1;
}
mod_cfg = ao2_global_obj_ref(module_configs);
-
- if (!ast_test_flag(&mod_cfg->general->settings, CDR_ENABLED) ||
- !(ast_test_flag(&mod_cfg->general->settings, CDR_BATCHMODE))) {
+ if (!mod_cfg
+ || !ast_test_flag(&mod_cfg->general->settings, CDR_ENABLED)
+ || !ast_test_flag(&mod_cfg->general->settings, CDR_BATCHMODE)) {
/* If batch mode used to be enabled, finalize the batch */
if (ast_test_flag(&old_mod_cfg->general->settings, CDR_BATCHMODE)) {
finalize_batch_mode();
}
}
+ ao2_cleanup(mod_cfg);
+ ao2_cleanup(old_mod_cfg);
return cdr_toggle_runtime_options();
}
-
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "CDR Engine",
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload_module,
+ .load_pri = AST_MODPRI_CORE,
+ .requires = "extconfig",
+);