[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