[asterisk-commits] mjordan: branch mjordan/cdrs-of-doom r382749 - in /team/mjordan/cdrs-of-doom:...
SVN commits to the Asterisk project
asterisk-commits at lists.digium.com
Fri Mar 8 14:54:57 CST 2013
Author: mjordan
Date: Fri Mar 8 14:54:54 2013
New Revision: 382749
URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=382749
Log:
Create the branch for CDRs in Asterisk 12
Added:
team/mjordan/cdrs-of-doom/
- copied from r382746, trunk/
Modified:
team/mjordan/cdrs-of-doom/include/asterisk/test.h
team/mjordan/cdrs-of-doom/main/cdr.c
team/mjordan/cdrs-of-doom/main/channel.c
team/mjordan/cdrs-of-doom/main/test.c
Modified: team/mjordan/cdrs-of-doom/include/asterisk/test.h
URL: http://svnview.digium.com/svn/asterisk/team/mjordan/cdrs-of-doom/include/asterisk/test.h?view=diff&rev=382749&r1=382746&r2=382749
==============================================================================
--- team/mjordan/cdrs-of-doom/include/asterisk/test.h (original)
+++ team/mjordan/cdrs-of-doom/include/asterisk/test.h Fri Mar 8 14:54:54 2013
@@ -267,6 +267,9 @@
*/
void ast_test_debug(struct ast_test *test, const char *fmt, ...) __attribute__((format(printf, 2, 3)));
+void ast_test_set_result(struct ast_test *test, enum ast_test_result_state state);
+
+
/*!
* \brief update test's status during testing.
*
Modified: team/mjordan/cdrs-of-doom/main/cdr.c
URL: http://svnview.digium.com/svn/asterisk/team/mjordan/cdrs-of-doom/main/cdr.c?view=diff&rev=382749&r1=382746&r2=382749
==============================================================================
--- team/mjordan/cdrs-of-doom/main/cdr.c (original)
+++ team/mjordan/cdrs-of-doom/main/cdr.c Fri Mar 8 14:54:54 2013
@@ -62,13 +62,285 @@
#include "asterisk/cli.h"
#include "asterisk/stringfields.h"
#include "asterisk/data.h"
+#include "asterisk/config_options.h"
+#include "asterisk/stasis.h"
/*** DOCUMENTATION
+ <configInfo name="cdr" language="en_US">
+ <synopsis>Call Detail Record configuration</synopsis>
+ <description>
+ <para>CDR is Call Detail Record, which provides logging services via a variety of
+ pluggable backend modules. Detailed call information can be recorded to
+ databases, files, etc. Useful for billing, fraud prevention, compliance with
+ Sarbanes-Oxley aka The Enron Act, QOS evaluations, and more.</para>
+ </description>
+ <configFile name="cdr.conf">
+ <configObject name="general">
+ <synopsis>Global settings applied to the CDR engine.</synopsis>
+ <configOption name="enable">
+ <synopsis>Enable/disable CDR logging.</synopsis>
+ <description><para>Define whether or not to use CDR logging. Setting this to "no" will override
+ any loading of backend CDR modules. Default is "yes".</para>
+ </description>
+ </configOption>
+ <configOption name="unanswered">
+ <synopsis>Log calls that are never answered.</synopsis>
+ <description><para>Define whether or not to log unanswered calls. Setting this to "yes" will
+ report every attempt to ring a phone in dialing attempts, when it was not
+ answered. For example, if you try to dial 3 extensions, and this option is "yes",
+ you will get 3 CDR's, one for each phone that was rung. Some find this information horribly
+ useless. Others find it very valuable. Note, in "yes" mode, you will see one CDR, with one of
+ the call targets on one side, and the originating channel on the other, and then one CDR for
+ each channel attempted. This may seem redundant, but cannot be helped.</para>
+ <para>In brief, this option controls the reporting of unanswered calls which only have an A
+ party. Calls which get offered to an outgoing line, but are unanswered, are still
+ logged, and that is the intended behavior. (It also results in some B side CDRs being
+ output, as they have the B side channel as their source channel, and no destination
+ channel.)</para>
+ </description>
+ </configOption>
+ <configOption name="congestion">
+ <synopsis>Log congested calls.</synopsis>
+ <description><para>Define whether or not to log congested calls. Setting this to "yes" will
+ report each call that fails to complete due to congestion conditions.</para>
+ </description>
+ </configOption>
+ <configOption name="endbeforehexten">
+ <synopsis>End the CDR before executing the "h" extension</synopsis>
+ <description><para>Normally, CDR's are not closed out until after all extensions are finished
+ executing. By enabling this option, the CDR will be ended before executing
+ the <literal>h</literal> extension and hangup handlers so that CDR values such as <literal>end</literal> and
+ <literal>"billsec"</literal> may be retrieved inside of of this extension.
+ The default value is "no".</para>
+ </description>
+ </configOption>
+ <configOption name="initiatedseconds">
+ <synopsis>Count microseconds for billsec purposes</synopsis>
+ <description><para>Normally, the <literal>billsec</literal> field logged to the CDR backends
+ is simply the end time (hangup time) minus the answer time in seconds. Internally,
+ asterisk stores the time in terms of microseconds and seconds. By setting
+ initiatedseconds to <literal>yes</literal>, you can force asterisk to report any seconds
+ that were initiated (a sort of round up method). Technically, this is
+ when the microsecond part of the end time is greater than the microsecond
+ part of the answer time, then the billsec time is incremented one second.</para>
+ </description>
+ </configOption>
+ <configOption name="batch">
+ <synopsis>Submit CDRs to the backends for processing in batches</synopsis>
+ <description><para>Define the CDR batch mode, where instead of posting the CDR at the end of
+ every call, the data will be stored in a buffer to help alleviate load on the
+ asterisk server.</para>
+ <warning><para>Use of batch mode may result in data loss after unsafe asterisk termination,
+ i.e., software crash, power failure, kill -9, etc.</para>
+ </warning>
+ </description>
+ </configOption>
+ <configOption name="size">
+ <synopsis>The maximum number of CDRs to accumulate before triggering a batch</synopsis>
+ <description><para>Define the maximum number of CDRs to accumulate in the buffer before posting
+ them to the backend engines. batch must be set to <literal>yes</literal>.</para>
+ </description>
+ </configOption>
+ <configOption name="time">
+ <synopsis>The maximum time to accumulate CDRs before triggering a batch</synopsis>
+ <description><para>Define the maximum time to accumulate CDRs before posting them in a batch to the
+ backend engines. If this time limit is reached, then it will post the records, regardless of the value
+ defined for size. batch must be set to <literal>yes</literal>.</para>
+ <note><para>Time is expressed in seconds.</para></note>
+ </description>
+ </configOption>
+ <configOption name="scheduleronly">
+ <synopsis>Post batched CDRs on their own thread instead of the scheduler</synopsis>
+ <description><para>The CDR engine uses the internal asterisk scheduler to determine when to post
+ records. Posting can either occur inside the scheduler thread, or a new
+ thread can be spawned for the submission of every batch. For small batches,
+ it might be acceptable to just use the scheduler thread, so set this to <literal>yes</literal>.
+ For large batches, say anything over size=10, a new thread is recommended, so
+ set this to <literal>no</literal>.</para>
+ </description>
+ </configOption>
+ <configOption name="safeshutdown">
+ <synopsis>Block shutdown of Asterisk until CDRs are submitted</synopsis>
+ <description><para>When shutting down asterisk, you can block until the CDRs are submitted. If
+ you don't, then data will likely be lost. You can always check the size of
+ the CDR batch buffer with the CLI <astcli>cdr status</astcli> command. To enable blocking on
+ submission of CDR data during asterisk shutdown, set this to <literal>yes</literal>.</para>
+ </description>
+ </configOption>
+ </configObject>
+ </configFile>
+ </configInfo>
***/
+
+#define DEFAULT_ENABLED "1"
+#define DEFAULT_BATCHMODE "0"
+#define DEFAULT_UNANSWERED "0"
+#define DEFAULT_CONGESTION "0"
+#define DEFAULT_AMA_FLAGS AST_CDR_DOCUMENTATION
+#define DEFAULT_END_BEFORE_H_EXTEN "0"
+#define DEFAULT_INITIATED_SECONDS "0"
+
+#define DEFAULT_BATCH_SIZE "100"
+#define MAX_BATCH_SIZE 1000
+#define DEFAULT_BATCH_TIME "300"
+#define MAX_BATCH_TIME 86400
+#define DEFAULT_BATCH_SCHEDULER_ONLY "0"
+#define DEFAULT_BATCH_SAFE_SHUTDOWN "1"
+
/*! Default AMA flag for billing records (CDR's) */
-int ast_default_amaflags = AST_CDR_DOCUMENTATION;
-char ast_default_accountcode[AST_MAX_ACCOUNT_CODE];
+int ast_default_amaflags = DEFAULT_AMA_FLAGS;
+
+enum cdr_settings {
+ CDR_ENABLED = 1 << 0, /*< Enable CDRs */
+ CDR_BATCHMODE = 1 << 1, /*< Whether or not we should dispatch CDRs in batches */
+ CDR_UNANSWERED = 1 << 2, /*< Log unanswered CDRs */
+ CDR_CONGESTION = 1 << 3, /*< Treat congestion as if it were a failed call */
+ CDR_END_BEFORE_H_EXTEN = 1 << 4, /*< End the CDR before the 'h' extension runs */
+ CDR_INITIATED_SECONDS = 1 << 5, /*< Include microseconds into the billing time */
+};
+
+enum batch_mode_settings {
+ BATCH_MODE_SCHEDULER_ONLY = 1 << 0, /*< Don't spawn a thread to handle the batches - do it on the scheduler */
+ BATCH_MODE_SAFE_SHUTDOWN = 1 << 1, /*< During safe shutdown, submit the batched CDRs */
+};
+
+static struct stasis_subscription *channel_state_sub;
+
+struct cdr_object;
+
+struct cdr_object_fn_table {
+ void (* const init_function)(struct cdr_object *cdr);
+ void (* const process_channel_message)(struct cdr_object *cdr, struct ast_channel_snapshot *snapshot);
+ void (* const process_varset_message)(struct cdr_object *cdr, struct ast_channel_snapshot *snapshot, const char *name, const char *value);
+};
+
+static void single_active_state_process_channel_message(struct cdr_object *cdr, struct ast_channel_snapshot *snapshot);
+struct cdr_object_fn_table single_active_state_fn_table = {
+ .process_channel_message = single_active_state_process_channel_message,
+};
+
+struct cdr_object_fn_table hangup_state_fn_table = {
+ .process_channel_message = NULL,
+};
+
+struct cdr_object {
+ struct ast_channel_snapshot *party_a;
+ struct varshead party_a_variables;
+ struct ast_channel_snapshot *party_b;
+ struct varshead party_b_variables;
+ struct cdr_object_fn_table *fn_table;
+
+ AST_LIST_ENTRY(cdr_object) list;
+};
+
+/* NON-INHERITABLE FUNCTIONS */
+
+static void transition_state(struct cdr_object *cdr, cdr_object_fn_table *fn_table)
+{
+ cdr->fn_table = fn_table;
+ if (cdr->fn_table->init_function) {
+ cdr->fn_table->init_function(cdr);
+ }
+}
+
+static void check_for_hangup(struct cdr_object *cdr)
+{
+ if (ast_test_flag(&cdr->party_a->flags, AST_FLAG_ZOMBIE)) {
+ transition_state(cdr, &hangup_state_fn_table);
+ }
+}
+
+/* SINGLE STATE */
+
+static void single_active_state_process_channel_message(struct cdr_object *cdr, struct ast_channel_snapshot *snapshot)
+{
+ ast_assert(cdr != NULL);
+ ast_assert(snapshot != NULL);
+
+ if (!strcmp(snapshot->name, cdr->party_a->name)) {
+ ao2_ref(cdr->party_a, -1);
+ cdr->party_a = snapshot;
+ ao2_ref(cdr->party_a, +1);
+ }
+ check_for_hangup();
+}
+
+
+
+/*! \brief The global options available for CDRs */
+struct cdr_config {
+ struct ast_flags settings; /*< CDR settings */
+ struct batch_settings {
+ unsigned int time; /*< Time between batches */
+ unsigned int size; /*< Size to trigger a batch */
+ struct ast_flags settings; /*< Settings for batches */
+ } batch_settings;
+};
+
+/*! \brief The configuration settings for this module */
+struct module_config {
+ struct cdr_config *general; /*< CDR global settings */
+};
+
+/*! \brief The container for the module configuration */
+static AO2_GLOBAL_OBJ_STATIC(module_configs);
+
+/*! \brief The type definition for general options */
+static struct aco_type general_option = {
+ .type = ACO_GLOBAL,
+ .name = "general",
+ .item_offset = offsetof(struct module_config, general),
+ .category = "^general$",
+ .category_match = ACO_WHITELIST,
+};
+
+static void *module_config_alloc(void);
+static void module_config_destructor(void *obj);
+
+/*! \brief The file definition */
+static struct aco_file module_file_conf = {
+ .filename = "cdr.conf", /*< The name of the config file */
+ .types = ACO_TYPES(&general_option), /*< The type to process */
+};
+
+CONFIG_INFO_CORE("cdr", cfg_info, module_configs, module_config_alloc,
+ .files = ACO_FILES(&module_file_conf),
+);
+
+static struct aco_type *general_options[] = ACO_TYPES(&general_option);
+
+/*! \brief Dispose of a module config object */
+static void module_config_destructor(void *obj)
+{
+ struct module_config *cfg = obj;
+
+ if (!cfg) {
+ return;
+ }
+ ao2_ref(cfg->general, -1);
+}
+
+/*! \brief Create a new module config object */
+static void *module_config_alloc(void)
+{
+ struct module_config *mod_cfg;
+ struct cdr_config *cdr_config;
+
+ mod_cfg = ao2_alloc(sizeof(*mod_cfg), module_config_destructor);
+ if (!mod_cfg) {
+ return NULL;
+ }
+
+ cdr_config = ao2_alloc(sizeof(*cdr_config), NULL);
+ if (!cdr_config) {
+ ao2_ref(cdr_config, -1);
+ return NULL;
+ }
+ mod_cfg->general = cdr_config;
+
+ return mod_cfg;
+}
struct ast_cdr_beitem {
char name[20];
@@ -99,30 +371,6 @@
static int cdr_sched = -1;
static pthread_t cdr_thread = AST_PTHREADT_NULL;
-static int enabled;
-static const int ENABLED_DEFAULT = 1;
-
-static int batchmode;
-static const int BATCHMODE_DEFAULT = 0;
-
-static int unanswered;
-static const int UNANSWERED_DEFAULT = 0;
-
-static int congestion;
-static const int CONGESTION_DEFAULT = 0;
-
-static int batchsize;
-static const int BATCH_SIZE_DEFAULT = 100;
-
-static int batchtime;
-static const int BATCH_TIME_DEFAULT = 300;
-
-static int batchscheduleronly;
-static const int BATCH_SCHEDULER_ONLY_DEFAULT = 0;
-
-static int batchsafeshutdown;
-static const int BATCH_SAFE_SHUTDOWN_DEFAULT = 1;
-
AST_MUTEX_DEFINE_STATIC(cdr_batch_lock);
/* these are used to wake up the CDR thread when there's work to do */
@@ -131,7 +379,8 @@
int check_cdr_enabled(void)
{
- return enabled;
+ 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);
}
/*!
@@ -196,12 +445,14 @@
int ast_cdr_isset_unanswered(void)
{
- return unanswered;
+ RAII_VAR(struct module_config *, mod_cfg, ao2_global_obj_ref(module_configs), ao2_cleanup);
+ return ast_test_flag(&mod_cfg->general->settings, CDR_UNANSWERED);
}
int ast_cdr_isset_congestion(void)
{
- return congestion;
+ RAII_VAR(struct module_config *, mod_cfg, ao2_global_obj_ref(module_configs), ao2_cleanup);
+ return ast_test_flag(&mod_cfg->general->settings, CDR_CONGESTION);
}
struct ast_cdr *ast_cdr_dup_unique(struct ast_cdr *cdr)
@@ -804,14 +1055,16 @@
void ast_cdr_congestion(struct ast_cdr *cdr)
{
+ RAII_VAR(struct module_config *, mod_cfg, ao2_global_obj_ref(module_configs), ao2_cleanup);
char *chan;
/* if congestion log is disabled, pass the buck to ast_cdr_failed */
- if (!congestion) {
+ if (!ast_test_flag(&mod_cfg->general->settings, CDR_CONGESTION)) {
ast_cdr_failed(cdr);
- }
-
- while (cdr && congestion) {
+ return;
+ }
+
+ while (cdr) {
if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
chan = !ast_strlen_zero(cdr->channel) ? cdr->channel : "<unknown>";
@@ -949,7 +1202,7 @@
cdr_seq_inc(cdr);
cdr->disposition = (ast_channel_state(c) == AST_STATE_UP) ? AST_CDR_ANSWERED : AST_CDR_NOANSWER;
- cdr->amaflags = ast_channel_amaflags(c) ? ast_channel_amaflags(c) : ast_default_amaflags;
+ cdr->amaflags = ast_channel_amaflags(c) ? ast_channel_amaflags(c) : DEFAULT_AMA_FLAGS;
ast_copy_string(cdr->accountcode, ast_channel_accountcode(c), sizeof(cdr->accountcode));
ast_copy_string(cdr->peeraccount, ast_channel_peeraccount(c), sizeof(cdr->peeraccount));
/* Destination information */
@@ -1176,10 +1429,13 @@
static void post_cdr(struct ast_cdr *cdr)
{
+ RAII_VAR(struct module_config *, mod_cfg, ao2_global_obj_ref(module_configs), ao2_cleanup);
struct ast_cdr_beitem *i;
for ( ; cdr ; cdr = cdr->next) {
- if (!unanswered && cdr->disposition < AST_CDR_ANSWERED && (ast_strlen_zero(cdr->channel) || ast_strlen_zero(cdr->dstchannel))) {
+ 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))) {
/* For people, who don't want to see unanswered single-channel events */
ast_set_flag(cdr, AST_CDR_FLAG_POST_DISABLED);
continue;
@@ -1328,6 +1584,7 @@
void ast_cdr_submit_batch(int do_shutdown)
{
+ RAII_VAR(struct module_config *, mod_cfg, ao2_global_obj_ref(module_configs), ao2_cleanup);
struct ast_cdr_batch_item *oldbatchitems = NULL;
pthread_t batch_post_thread = AST_PTHREADT_NULL;
@@ -1343,7 +1600,7 @@
/* 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 (batchscheduleronly || do_shutdown) {
+ if (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 {
@@ -1358,9 +1615,10 @@
static int submit_scheduled_batch(const void *data)
{
+ RAII_VAR(struct module_config *, mod_cfg, ao2_global_obj_ref(module_configs), ao2_cleanup);
ast_cdr_submit_batch(0);
/* manually reschedule from this point in time */
- cdr_sched = ast_sched_add(sched, batchtime * 1000, submit_scheduled_batch, NULL);
+ cdr_sched = ast_sched_add(sched, mod_cfg->general->batch_settings.time * 1000, submit_scheduled_batch, NULL);
/* returning zero so the scheduler does not automatically reschedule */
return 0;
}
@@ -1381,12 +1639,14 @@
{
struct ast_cdr_batch_item *newtail;
int curr;
-
- if (!cdr)
+ RAII_VAR(struct module_config *, mod_cfg, ao2_global_obj_ref(module_configs), ao2_cleanup);
+
+ if (!cdr) {
return;
+ }
/* maybe they disabled CDR stuff completely, so just drop it */
- if (!enabled) {
+ if (!ast_test_flag(&mod_cfg->general->settings, CDR_ENABLED)) {
ast_debug(1, "Dropping CDR !\n");
ast_set_flag(cdr, AST_CDR_FLAG_POST_DISABLED);
ast_cdr_free(cdr);
@@ -1394,7 +1654,7 @@
}
/* post stuff immediately if we are not in batch mode, this is legacy behaviour */
- if (!batchmode) {
+ if (!ast_test_flag(&mod_cfg->general->settings, CDR_BATCHMODE)) {
post_cdr(cdr);
ast_cdr_free(cdr);
return;
@@ -1426,7 +1686,7 @@
curr = batch->size++;
/* if we have enough stuff to post, then do it */
- if (curr >= (batchsize - 1)) {
+ if (curr >= (mod_cfg->general->batch_settings.size - 1)) {
submit_unscheduled_batch();
}
@@ -1461,9 +1721,10 @@
static char *handle_cli_status(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
- struct ast_cdr_beitem *beitem=NULL;
- int cnt=0;
- long nextbatchtime=0;
+ struct ast_cdr_beitem *beitem = NULL;
+ RAII_VAR(struct module_config *, mod_cfg, ao2_global_obj_ref(module_configs), ao2_cleanup);
+ int cnt = 0;
+ long nextbatchtime = 0;
switch (cmd) {
case CLI_INIT:
@@ -1482,23 +1743,23 @@
ast_cli(a->fd, "\n");
ast_cli(a->fd, "Call Detail Record (CDR) settings\n");
ast_cli(a->fd, "----------------------------------\n");
- ast_cli(a->fd, " Logging: %s\n", enabled ? "Enabled" : "Disabled");
- ast_cli(a->fd, " Mode: %s\n", batchmode ? "Batch" : "Simple");
- if (enabled) {
- ast_cli(a->fd, " Log unanswered calls: %s\n", unanswered ? "Yes" : "No");
- ast_cli(a->fd, " Log congestion: %s\n\n", congestion ? "Yes" : "No");
- if (batchmode) {
+ ast_cli(a->fd, " Logging: %s\n", ast_test_flag(&mod_cfg->general->settings, CDR_ENABLED) ? "Enabled" : "Disabled");
+ ast_cli(a->fd, " Mode: %s\n", ast_test_flag(&mod_cfg->general->settings, CDR_BATCHMODE) ? "Batch" : "Simple");
+ if (ast_test_flag(&mod_cfg->general->settings, CDR_ENABLED)) {
+ ast_cli(a->fd, " Log unanswered calls: %s\n", ast_test_flag(&mod_cfg->general->settings, CDR_UNANSWERED) ? "Yes" : "No");
+ ast_cli(a->fd, " Log congestion: %s\n\n", ast_test_flag(&mod_cfg->general->settings, CDR_CONGESTION) ? "Yes" : "No");
+ if (ast_test_flag(&mod_cfg->general->settings, CDR_BATCHMODE)) {
ast_cli(a->fd, "* Batch Mode Settings\n");
ast_cli(a->fd, " -------------------\n");
if (batch)
cnt = batch->size;
if (cdr_sched > -1)
nextbatchtime = ast_sched_when(sched, cdr_sched);
- ast_cli(a->fd, " Safe shutdown: %s\n", batchsafeshutdown ? "Enabled" : "Disabled");
- ast_cli(a->fd, " Threading model: %s\n", batchscheduleronly ? "Scheduler only" : "Scheduler plus separate threads");
+ ast_cli(a->fd, " Safe shutdown: %s\n", ast_test_flag(&mod_cfg->general->batch_settings.settings, BATCH_MODE_SAFE_SHUTDOWN) ? "Enabled" : "Disabled");
+ ast_cli(a->fd, " Threading model: %s\n", ast_test_flag(&mod_cfg->general->batch_settings.settings, BATCH_MODE_SCHEDULER_ONLY) ? "Scheduler only" : "Scheduler plus separate threads");
ast_cli(a->fd, " Current batch size: %d record%s\n", cnt, ESS(cnt));
- ast_cli(a->fd, " Maximum batch size: %d record%s\n", batchsize, ESS(batchsize));
- ast_cli(a->fd, " Maximum batch time: %d second%s\n", batchtime, ESS(batchtime));
+ ast_cli(a->fd, " Maximum batch size: %d record%s\n", mod_cfg->general->batch_settings.size, ESS(mod_cfg->general->batch_settings.size));
+ ast_cli(a->fd, " Maximum batch time: %d second%s\n", mod_cfg->general->batch_settings.time, ESS(mod_cfg->general->batch_settings.time));
ast_cli(a->fd, " Next batch processing time: %ld second%s\n\n", nextbatchtime, ESS(nextbatchtime));
}
ast_cli(a->fd, "* Registered Backends\n");
@@ -1542,133 +1803,68 @@
static struct ast_cli_entry cli_submit = AST_CLI_DEFINE(handle_cli_submit, "Posts all pending batched CDR data");
static struct ast_cli_entry cli_status = AST_CLI_DEFINE(handle_cli_status, "Display the CDR status");
-static void do_reload(int reload)
-{
- struct ast_config *config;
- struct ast_variable *v;
- int cfg_size;
- int cfg_time;
- int was_enabled;
- int was_batchmode;
- struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
-
- if ((config = ast_config_load2("cdr.conf", "cdr", config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
+static void finalize_batch_mode(void)
+{
+ if (cdr_thread == AST_PTHREADT_NULL) {
return;
}
-
- ast_mutex_lock(&cdr_batch_lock);
-
- was_enabled = enabled;
- was_batchmode = batchmode;
-
- batchsize = BATCH_SIZE_DEFAULT;
- batchtime = BATCH_TIME_DEFAULT;
- batchscheduleronly = BATCH_SCHEDULER_ONLY_DEFAULT;
- batchsafeshutdown = BATCH_SAFE_SHUTDOWN_DEFAULT;
- enabled = ENABLED_DEFAULT;
- batchmode = BATCHMODE_DEFAULT;
- unanswered = UNANSWERED_DEFAULT;
- congestion = CONGESTION_DEFAULT;
-
- if (config == CONFIG_STATUS_FILEMISSING || config == CONFIG_STATUS_FILEINVALID) {
- ast_mutex_unlock(&cdr_batch_lock);
- return;
- }
-
- /* don't run the next scheduled CDR posting while reloading */
- AST_SCHED_DEL(sched, cdr_sched);
-
- for (v = ast_variable_browse(config, "general"); v; v = v->next) {
- if (!strcasecmp(v->name, "enable")) {
- enabled = ast_true(v->value);
- } else if (!strcasecmp(v->name, "unanswered")) {
- unanswered = ast_true(v->value);
- } else if (!strcasecmp(v->name, "congestion")) {
- congestion = ast_true(v->value);
- } else if (!strcasecmp(v->name, "batch")) {
- batchmode = ast_true(v->value);
- } else if (!strcasecmp(v->name, "scheduleronly")) {
- batchscheduleronly = ast_true(v->value);
- } else if (!strcasecmp(v->name, "safeshutdown")) {
- batchsafeshutdown = ast_true(v->value);
- } else if (!strcasecmp(v->name, "size")) {
- if (sscanf(v->value, "%30d", &cfg_size) < 1) {
- ast_log(LOG_WARNING, "Unable to convert '%s' to a numeric value.\n", v->value);
- } else if (cfg_size < 0) {
- ast_log(LOG_WARNING, "Invalid maximum batch size '%d' specified, using default\n", cfg_size);
- } else {
- batchsize = cfg_size;
- }
- } else if (!strcasecmp(v->name, "time")) {
- if (sscanf(v->value, "%30d", &cfg_time) < 1) {
- ast_log(LOG_WARNING, "Unable to convert '%s' to a numeric value.\n", v->value);
- } else if (cfg_time < 0) {
- ast_log(LOG_WARNING, "Invalid maximum batch time '%d' specified, using default\n", cfg_time);
- } else {
- batchtime = cfg_time;
- }
- } else if (!strcasecmp(v->name, "endbeforehexten")) {
- ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_END_CDR_BEFORE_H_EXTEN);
- } else if (!strcasecmp(v->name, "initiatedseconds")) {
- ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_INITIATED_SECONDS);
- }
- }
-
- if (enabled && !batchmode) {
- ast_log(LOG_NOTICE, "CDR simple logging enabled.\n");
- } else if (enabled && batchmode) {
- cdr_sched = ast_sched_add(sched, batchtime * 1000, submit_scheduled_batch, NULL);
- ast_log(LOG_NOTICE, "CDR batch mode logging enabled, first of either size %d or time %d seconds.\n", batchsize, batchtime);
- } else {
- ast_log(LOG_NOTICE, "CDR logging disabled, data will be lost.\n");
- }
-
- /* if this reload enabled the CDR batch mode, create the background thread
- if it does not exist */
- if (enabled && batchmode && (!was_enabled || !was_batchmode) && (cdr_thread == AST_PTHREADT_NULL)) {
- ast_cond_init(&cdr_pending_cond, NULL);
- if (ast_pthread_create_background(&cdr_thread, NULL, do_cdr, NULL) < 0) {
- ast_log(LOG_ERROR, "Unable to start CDR thread.\n");
- AST_SCHED_DEL(sched, cdr_sched);
- } else {
- ast_cli_register(&cli_submit);
- ast_register_atexit(ast_cdr_engine_term);
- }
- /* if this reload disabled the CDR and/or batch mode and there is a background thread,
- kill it */
- } else if (((!enabled && was_enabled) || (!batchmode && was_batchmode)) && (cdr_thread != AST_PTHREADT_NULL)) {
- /* wake up the thread so it will exit */
- pthread_cancel(cdr_thread);
- pthread_kill(cdr_thread, SIGURG);
- pthread_join(cdr_thread, NULL);
- cdr_thread = AST_PTHREADT_NULL;
- ast_cond_destroy(&cdr_pending_cond);
- ast_cli_unregister(&cli_submit);
- ast_unregister_atexit(ast_cdr_engine_term);
- /* if leaving batch mode, then post the CDRs in the batch,
- and don't reschedule, since we are stopping CDR logging */
- if (!batchmode && was_batchmode) {
- ast_cdr_engine_term();
- }
- }
-
- ast_mutex_unlock(&cdr_batch_lock);
- ast_config_destroy(config);
- manager_event(EVENT_FLAG_SYSTEM, "Reload", "Module: CDR\r\nMessage: CDR subsystem reload requested\r\n");
+ /* wake up the thread so it will exit */
+ pthread_cancel(cdr_thread);
+ pthread_kill(cdr_thread, SIGURG);
+ pthread_join(cdr_thread, NULL);
+ cdr_thread = AST_PTHREADT_NULL;
+ ast_cond_destroy(&cdr_pending_cond);
+ ast_cli_unregister(&cli_submit);
+ ast_unregister_atexit(ast_cdr_engine_term);
+ ast_cdr_engine_term();
+}
+
+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, "enable", ACO_EXACT, general_options, DEFAULT_ENABLED, OPT_BOOLFLAG_T, 1, FLDSET(struct cdr_config, settings), CDR_ENABLED);
+ aco_option_register(&cfg_info, "unanswered", ACO_EXACT, general_options, DEFAULT_UNANSWERED, OPT_BOOLFLAG_T, 1, FLDSET(struct cdr_config, settings), CDR_UNANSWERED);
+ aco_option_register(&cfg_info, "congestion", ACO_EXACT, general_options, DEFAULT_CONGESTION, OPT_BOOLFLAG_T, 1, FLDSET(struct cdr_config, settings), CDR_CONGESTION);
+ aco_option_register(&cfg_info, "batch", ACO_EXACT, general_options, DEFAULT_BATCHMODE, OPT_BOOLFLAG_T, 1, FLDSET(struct cdr_config, settings), CDR_BATCHMODE);
+ aco_option_register(&cfg_info, "endbeforehexten", ACO_EXACT, general_options, DEFAULT_END_BEFORE_H_EXTEN, OPT_BOOLFLAG_T, 1, FLDSET(struct cdr_config, settings), CDR_END_BEFORE_H_EXTEN);
+ aco_option_register(&cfg_info, "initiatedseconds", ACO_EXACT, general_options, DEFAULT_INITIATED_SECONDS, OPT_BOOLFLAG_T, 1, FLDSET(struct cdr_config, settings), CDR_INITIATED_SECONDS);
+ aco_option_register(&cfg_info, "scheduleronly", ACO_EXACT, general_options, DEFAULT_BATCH_SCHEDULER_ONLY, OPT_BOOLFLAG_T, 1, FLDSET(struct 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 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 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 cdr_config, batch_settings.time), 0, MAX_BATCH_TIME);
+ }
+
+ if (aco_process_config(&cfg_info, reload)) {
+ if (!mod_cfg) {
+ 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(module_configs, mod_cfg);
+ return 0;
+ }
+ return 1;
+ }
+
+ if (reload) {
+ manager_event(EVENT_FLAG_SYSTEM, "Reload", "Module: CDR\r\nMessage: CDR subsystem reload requested\r\n");
+ }
+ return 0;
}
static void cdr_engine_shutdown(void)
{
- if (cdr_thread != AST_PTHREADT_NULL) {
- /* wake up the thread so it will exit */
- pthread_cancel(cdr_thread);
- pthread_kill(cdr_thread, SIGURG);
- pthread_join(cdr_thread, NULL);
- cdr_thread = AST_PTHREADT_NULL;
- ast_cond_destroy(&cdr_pending_cond);
- }
- ast_cli_unregister(&cli_submit);
-
+ finalize_batch_mode();
+ aco_info_destroy(&cfg_info);
ast_cli_unregister(&cli_status);
ast_sched_context_destroy(sched);
sched = NULL;
@@ -1676,8 +1872,40 @@
batch = NULL;
}
+static void cdr_enable_batch_mode(struct 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);
+ if (ast_pthread_create_background(&cdr_thread, NULL, do_cdr, NULL) < 0) {
+ ast_log(LOG_ERROR, "Unable to start CDR thread.\n");
+ return;
+ }
+ ast_cli_register(&cli_submit);
+ ast_register_atexit(ast_cdr_engine_term);
+ }
+
+ /* 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);
+ ast_log(LOG_NOTICE, "CDR batch mode logging enabled, first of either size %d or time %d seconds.\n",
+ config->batch_settings.size, config->batch_settings.time);
+}
+
int ast_cdr_engine_init(void)
{
+ RAII_VAR(struct module_config *, mod_cfg, NULL, ao2_cleanup);
+
+ if (process_config(0)) {
+ return -1;
+ }
+
+ channel_state_sub = stasis_subscribe(
+ stasis_caching_get_topic(ast_channel_events_all_cached()),
+ channel_message_cb, NULL);
+
sched = ast_sched_context_create();
if (!sched) {
ast_log(LOG_ERROR, "Unable to create schedule context.\n");
@@ -1685,8 +1913,19 @@
}
ast_cli_register(&cli_status);
- do_reload(0);
ast_register_atexit(cdr_engine_shutdown);
+
+ mod_cfg = ao2_global_obj_ref(module_configs);
+ ast_log(LOG_NOTICE, "%p %p \n", mod_cfg, mod_cfg->general);
+ if (ast_test_flag(&mod_cfg->general->settings, CDR_ENABLED)) {
+ if (ast_test_flag(&mod_cfg->general->settings, CDR_BATCHMODE)) {
+ cdr_enable_batch_mode(mod_cfg->general);
+ } else {
+ ast_log(LOG_NOTICE, "CDR simple logging enabled.\n");
+ }
+ } else {
+ ast_log(LOG_NOTICE, "CDR logging disabled.\n");
+ }
return 0;
}
@@ -1695,12 +1934,40 @@
hanging up channels, and then again, after the channel hangup timeout expires */
void ast_cdr_engine_term(void)
{
- ast_cdr_submit_batch(batchsafeshutdown);
+ RAII_VAR(struct module_config *, mod_cfg, ao2_global_obj_ref(module_configs), ao2_cleanup);
+
+ ast_cdr_submit_batch(ast_test_flag(&mod_cfg->general->batch_settings.settings, BATCH_MODE_SAFE_SHUTDOWN));
}
int ast_cdr_engine_reload(void)
{
- do_reload(1);
+ 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);
+
+ if (process_config(1)) {
+ 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 batch mode used to be enabled, finalize the batch */
+ if (ast_test_flag(&old_mod_cfg->general->settings, CDR_BATCHMODE)) {
+ finalize_batch_mode();
+ }
+ }
+
+ if (ast_test_flag(&mod_cfg->general->settings, CDR_ENABLED)) {
+ if (!ast_test_flag(&mod_cfg->general->settings, CDR_BATCHMODE)) {
+ ast_log(LOG_NOTICE, "CDR simple logging enabled.\n");
+ } else {
+ cdr_enable_batch_mode(mod_cfg->general);
+ }
+ } else {
+ ast_log(LOG_NOTICE, "CDR logging disabled, data will be lost.\n");
+ }
+
return 0;
}
Modified: team/mjordan/cdrs-of-doom/main/channel.c
URL: http://svnview.digium.com/svn/asterisk/team/mjordan/cdrs-of-doom/main/channel.c?view=diff&rev=382749&r1=382746&r2=382749
==============================================================================
--- team/mjordan/cdrs-of-doom/main/channel.c (original)
+++ team/mjordan/cdrs-of-doom/main/channel.c Fri Mar 8 14:54:54 2013
@@ -1190,10 +1190,9 @@
ast_channel_amaflags_set(tmp, ast_default_amaflags);
}
- if (!ast_strlen_zero(acctcode))
+ if (!ast_strlen_zero(acctcode)) {
ast_channel_accountcode_set(tmp, acctcode);
- else
- ast_channel_accountcode_set(tmp, ast_default_accountcode);
+ }
ast_channel_context_set(tmp, S_OR(context, "default"));
ast_channel_exten_set(tmp, S_OR(exten, "s"));
Modified: team/mjordan/cdrs-of-doom/main/test.c
URL: http://svnview.digium.com/svn/asterisk/team/mjordan/cdrs-of-doom/main/test.c?view=diff&rev=382749&r1=382746&r2=382749
==============================================================================
--- team/mjordan/cdrs-of-doom/main/test.c (original)
+++ team/mjordan/cdrs-of-doom/main/test.c Fri Mar 8 14:54:54 2013
@@ -199,6 +199,14 @@
test->time = ast_tvdiff_ms(ast_tvnow(), begin);
}
+void ast_test_set_result(struct ast_test *test, enum ast_test_result_state state)
+{
+ if (test->state == AST_TEST_FAIL || state == AST_TEST_NOT_RUN) {
+ return;
+ }
+ test->state = state;
+}
+
static void test_xml_entry(struct ast_test *test, FILE *f)
{
if (!f || !test || test->state == AST_TEST_NOT_RUN) {
More information about the asterisk-commits
mailing list