[asterisk-commits] russell: branch russell/cdr-q r248229 - in /team/russell/cdr-q: configs/ incl...
SVN commits to the Asterisk project
asterisk-commits at lists.digium.com
Mon Feb 22 01:02:08 CST 2010
Author: russell
Date: Mon Feb 22 01:02:04 2010
New Revision: 248229
URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=248229
Log:
Save off some code.
Modified:
team/russell/cdr-q/configs/cdr.conf.sample
team/russell/cdr-q/include/asterisk/cdr.h
team/russell/cdr-q/main/asterisk.c
team/russell/cdr-q/main/cdr.c
team/russell/cdr-q/main/features.c
team/russell/cdr-q/main/manager.c
Modified: team/russell/cdr-q/configs/cdr.conf.sample
URL: http://svnview.digium.com/svn/asterisk/team/russell/cdr-q/configs/cdr.conf.sample?view=diff&rev=248229&r1=248228&r2=248229
==============================================================================
--- team/russell/cdr-q/configs/cdr.conf.sample (original)
+++ team/russell/cdr-q/configs/cdr.conf.sample Mon Feb 22 01:02:04 2010
@@ -22,42 +22,6 @@
; channel on the other, and then one CDR for each channel attempted. This may seem
; redundant, but cannot be helped.
;unanswered = no
-
-; 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. Default is "no".
-;
-; WARNING WARNING WARNING
-; Use of batch mode may result in data loss after unsafe asterisk termination
-; ie. software crash, power failure, kill -9, etc.
-; WARNING WARNING WARNING
-;
-;batch=no
-
-; Define the maximum number of CDRs to accumulate in the buffer before posting
-; them to the backend engines. 'batch' must be set to 'yes'. Default is 100.
-;size=100
-
-; Define the maximum time to accumulate CDRs in the buffer before posting them
-; 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
-; 'yes'. Note that time is in seconds. Default is 300 (5 minutes).
-;time=300
-
-; 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 "yes".
-; For large batches, say anything over size=10, a new thread is recommended, so
-; set this to "no". Default is "no".
-;scheduleronly=no
-
-; 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 "cdr status" command. To enable blocking on
-; submission of CDR data during asterisk shutdown, set this to "yes". Default
-; is "yes".
-;safeshutdown=yes
; 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
Modified: team/russell/cdr-q/include/asterisk/cdr.h
URL: http://svnview.digium.com/svn/asterisk/team/russell/cdr-q/include/asterisk/cdr.h?view=diff&rev=248229&r1=248228&r2=248229
==============================================================================
--- team/russell/cdr-q/include/asterisk/cdr.h (original)
+++ team/russell/cdr-q/include/asterisk/cdr.h Mon Feb 22 01:02:04 2010
@@ -16,8 +16,9 @@
* at the top of the source tree.
*/
-/*! \file
- * \brief Call Detail Record API
+/*!
+ * \file
+ * \brief Call Detail Record API
*/
#ifndef _ASTERISK_CDR_H
@@ -70,29 +71,29 @@
/*! Caller*ID with text */
char clid[AST_MAX_EXTENSION];
/*! Caller*ID number */
- char src[AST_MAX_EXTENSION];
+ char src[AST_MAX_EXTENSION];
/*! Destination extension */
- char dst[AST_MAX_EXTENSION];
+ char dst[AST_MAX_EXTENSION];
/*! Destination context */
- char dcontext[AST_MAX_EXTENSION];
-
+ char dcontext[AST_MAX_EXTENSION];
+
char channel[AST_MAX_EXTENSION];
/*! Destination channel if appropriate */
- char dstchannel[AST_MAX_EXTENSION];
+ char dstchannel[AST_MAX_EXTENSION];
/*! Last application if appropriate */
- char lastapp[AST_MAX_EXTENSION];
+ char lastapp[AST_MAX_EXTENSION];
/*! Last application data */
- char lastdata[AST_MAX_EXTENSION];
-
+ char lastdata[AST_MAX_EXTENSION];
+
struct timeval start;
-
+
struct timeval answer;
-
+
struct timeval end;
/*! Total time in system, in seconds */
- long int duration;
+ long int duration;
/*! Total time call is up, in seconds */
- long int billsec;
+ long int billsec;
/*! What happened to the call */
long int disposition;
/*! What flags to use */
@@ -128,38 +129,47 @@
/*!
* \brief CDR backend callback
+ *
* \warning CDR backends should NOT attempt to access the channel associated
* with a CDR record. This channel is not guaranteed to exist when the CDR
* backend is invoked.
*/
-typedef int (*ast_cdrbe)(struct ast_cdr *cdr);
-
-/*! \brief Return TRUE if CDR subsystem is enabled */
-int check_cdr_enabled(void);
-
-/*!
- * \brief Allocate a CDR record
+typedef int (*ast_cdr_post_cb)(struct ast_cdr *cdr);
+
+/*!
+ * \brief Return TRUE if CDR subsystem is enabled
+ */
+int ast_cdr_enabled(void);
+
+/*!
+ * \brief Allocate a CDR record
+ *
* \retval a malloc'd ast_cdr structure
* \retval NULL on error (malloc failure)
*/
struct ast_cdr *ast_cdr_alloc(void);
-/*!
+/*!
* \brief Duplicate a record and increment the sequence number.
+ *
* \param cdr the record to duplicate
- * \retval a malloc'd ast_cdr structure,
+ *
+ * \retval a malloc'd ast_cdr structure,
* \retval NULL on error (malloc failure)
+ *
* \see ast_cdr_dup()
* \see ast_cdr_dup_unique_swap()
*/
struct ast_cdr *ast_cdr_dup_unique(struct ast_cdr *cdr);
-/*!
- * \brief Duplicate a record and increment the sequence number of the old
- * record.
+/*!
+ * \brief Duplicate a record and increment the sequence number of the old record.
+ *
* \param cdr the record to duplicate
- * \retval a malloc'd ast_cdr structure,
+ *
+ * \retval a malloc'd ast_cdr structure,
* \retval NULL on error (malloc failure)
+ *
* \note This version increments the original CDR's sequence number rather than
* the duplicate's sequence number. The effect is as if the original CDR's
* sequence number was swapped with the duplicate's sequence number.
@@ -169,166 +179,191 @@
*/
struct ast_cdr *ast_cdr_dup_unique_swap(struct ast_cdr *cdr);
-/*!
- * \brief Duplicate a record
+/*!
+ * \brief Duplicate a record
+ *
* \param cdr the record to duplicate
- * \retval a malloc'd ast_cdr structure,
+ *
+ * \retval a malloc'd ast_cdr structure,
* \retval NULL on error (malloc failure)
+ *
* \see ast_cdr_dup_unique()
* \see ast_cdr_dup_unique_swap()
*/
struct ast_cdr *ast_cdr_dup(struct ast_cdr *cdr);
-/*!
- * \brief Free a CDR record
+/*!
+ * \brief Discard and free a CDR record
+ *
* \param cdr ast_cdr structure to free
- * Returns nothing
- */
-void ast_cdr_free(struct ast_cdr *cdr);
-
-/*!
- * \brief Discard and free a CDR record
- * \param cdr ast_cdr structure to free
- * Returns nothing -- same as free, but no checks or complaints
*/
void ast_cdr_discard(struct ast_cdr *cdr);
-/*!
+/*!
* \brief Initialize based on a channel
+ *
* \param cdr Call Detail Record to use for channel
* \param chan Channel to bind CDR with
+ *
* Initializes a CDR and associates it with a particular channel
+ *
* \return 0 by default
*/
int ast_cdr_init(struct ast_cdr *cdr, struct ast_channel *chan);
-/*!
- * \brief Initialize based on a channel
+/*!
+ * \brief Initialize based on a channel
+ *
* \param cdr Call Detail Record to use for channel
* \param chan Channel to bind CDR with
+ *
* Initializes a CDR and associates it with a particular channel
+ *
* \return 0 by default
*/
int ast_cdr_setcid(struct ast_cdr *cdr, struct ast_channel *chan);
-/*!
- * \brief Register a CDR handling engine
+/*!
+ * \brief Register a CDR handling engine
+ *
* \param name name associated with the particular CDR handler
* \param desc description of the CDR handler
* \param be function pointer to a CDR handler
+ *
* Used to register a Call Detail Record handler.
+ *
* \retval 0 on success.
* \retval -1 on error
*/
-int ast_cdr_register(const char *name, const char *desc, ast_cdrbe be);
-
-/*!
- * \brief Unregister a CDR handling engine
+int ast_cdr_register(const char *name, const char *desc, ast_cdr_post_cb post_cb);
+
+/*!
+ * \brief Unregister a CDR handling engine
+ *
* \param name name of CDR handler to unregister
+ *
* Unregisters a CDR by it's name
*/
void ast_cdr_unregister(const char *name);
-/*!
- * \brief Start a call
- * \param cdr the cdr you wish to associate with the call
+/*!
+ * \brief Start a call
+ *
+ * \param cdr the cdr you wish to associate with the call
+ *
* Starts all CDR stuff necessary for monitoring a call
* Returns nothing
*/
void ast_cdr_start(struct ast_cdr *cdr);
-/*! \brief Answer a call
- * \param cdr the cdr you wish to associate with the call
+/*!
+ * \brief Answer a call
+ *
+ * \param cdr the cdr you wish to associate with the call
+ *
* Starts all CDR stuff necessary for doing CDR when answering a call
+ *
* \note NULL argument is just fine.
*/
void ast_cdr_answer(struct ast_cdr *cdr);
-/*!
- * \brief A call wasn't answered
- * \param cdr the cdr you wish to associate with the call
+/*!
+ * \brief A call wasn't answered
+ *
+ * \param cdr the cdr you wish to associate with the call
+ *
* Marks the channel disposition as "NO ANSWER"
* Will skip CDR's in chain with ANS_LOCK bit set. (see
* forkCDR() application.
*/
extern void ast_cdr_noanswer(struct ast_cdr *cdr);
-/*!
- * \brief Busy a call
- * \param cdr the cdr you wish to associate with the call
+/*!
+ * \brief Busy a call
+ *
+ * \param cdr the cdr you wish to associate with the call
+ *
* Marks the channel disposition as "BUSY"
* Will skip CDR's in chain with ANS_LOCK bit set. (see
* forkCDR() application.
- * Returns nothing
*/
void ast_cdr_busy(struct ast_cdr *cdr);
-/*!
- * \brief Fail a call
- * \param cdr the cdr you wish to associate with the call
+/*!
+ * \brief Fail a call
+ *
+ * \param cdr the cdr you wish to associate with the call
+ *
* Marks the channel disposition as "FAILED"
* Will skip CDR's in chain with ANS_LOCK bit set. (see
* forkCDR() application.
- * Returns nothing
*/
void ast_cdr_failed(struct ast_cdr *cdr);
-/*!
+/*!
* \brief Save the result of the call based on the AST_CAUSE_*
+ *
* \param cdr the cdr you wish to associate with the call
* \param cause the AST_CAUSE_*
- * Returns nothing
*/
int ast_cdr_disposition(struct ast_cdr *cdr, int cause);
-
-/*!
+
+/*!
* \brief End a call
+ *
* \param cdr the cdr you have associated the call with
+ *
* Registers the end of call time in the cdr structure.
* Returns nothing
*/
void ast_cdr_end(struct ast_cdr *cdr);
-/*!
+/*!
* \brief Detaches the detail record for posting (and freeing) either now or at a
* later time in bulk with other records during batch mode operation.
+ *
* \param cdr Which CDR to detach from the channel thread
+ *
* Prevents the channel thread from blocking on the CDR handling
- * Returns nothing
*/
void ast_cdr_detach(struct ast_cdr *cdr);
-/*!
- * \brief Spawns (possibly) a new thread to submit a batch of CDRs to the backend engines
+/*!
+ * \brief Spawns (possibly) a new thread to submit a batch of CDRs to the backend engines
+ *
* \param shutdown Whether or not we are shutting down
+ *
* Blocks the asterisk shutdown procedures until the CDR data is submitted.
- * Returns nothing
*/
void ast_cdr_submit_batch(int shutdown);
-/*!
- * \brief Set the destination channel, if there was one
+/*!
+ * \brief Set the destination channel, if there was one
+ *
* \param cdr Which cdr it's applied to
* \param chan Channel to which dest will be
+ *
* Sets the destination channel the CDR is applied to
- * Returns nothing
*/
void ast_cdr_setdestchan(struct ast_cdr *cdr, const char *chan);
-/*!
- * \brief Set the last executed application
+/*!
+ * \brief Set the last executed application
+ *
* \param cdr which cdr to act upon
* \param app the name of the app you wish to change it to
* \param data the data you want in the data field of app you set it to
+ *
* Changes the value of the last executed app
- * Returns nothing
*/
void ast_cdr_setapp(struct ast_cdr *cdr, const char *app, const char *data);
/*!
* \brief Set the answer time for a call
+ *
* \param cdr the cdr you wish to associate with the call
* \param t the answer time
+ *
* Starts all CDR stuff necessary for doing CDR when answering a call
* NULL argument is just fine.
*/
@@ -336,92 +371,127 @@
/*!
* \brief Set the disposition for a call
+ *
* \param cdr the cdr you wish to associate with the call
* \param disposition the new disposition
+ *
* Set the disposition on a call.
* NULL argument is just fine.
*/
void ast_cdr_setdisposition(struct ast_cdr *cdr, long int disposition);
-/*!
- * \brief Convert a string to a detail record AMA flag
+/*!
+ * \brief Convert a string to a detail record AMA flag
+ *
* \param flag string form of flag
+ *
* Converts the string form of the flag to the binary form.
+ *
* \return the binary form of the flag
*/
int ast_cdr_amaflags2int(const char *flag);
-/*!
- * \brief Disposition to a string
+/*!
+ * \brief Disposition to a string
+ *
* \param disposition input binary form
+ *
* Converts the binary form of a disposition to string form.
+ *
* \return a pointer to the string form
*/
char *ast_cdr_disp2str(int disposition);
-/*!
- * \brief Reset the detail record, optionally posting it first
+/*!
+ * \brief Reset the detail record, optionally posting it first
+ *
* \param cdr which cdr to act upon
* \param flags |AST_CDR_FLAG_POSTED whether or not to post the cdr first before resetting it
* |AST_CDR_FLAG_LOCKED whether or not to reset locked CDR's
*/
void ast_cdr_reset(struct ast_cdr *cdr, struct ast_flags *flags);
-/*! Reset the detail record times, flags */
-/*!
+/*!
+ * \brief Reset the detail record times, flags
+ *
* \param cdr which cdr to act upon
* \param flags |AST_CDR_FLAG_POSTED whether or not to post the cdr first before resetting it
* |AST_CDR_FLAG_LOCKED whether or not to reset locked CDR's
*/
void ast_cdr_specialized_reset(struct ast_cdr *cdr, struct ast_flags *flags);
-/*! Flags to a string */
-/*!
+/*!
+ * \brief Convert amaflags to a string
+ *
* \param flags binary flag
+ *
* Converts binary flags to string flags
- * Returns string with flag name
+ *
+ * \return string with flag name
*/
char *ast_cdr_flags2str(int flags);
-/*!
+/*!
* \brief Move the non-null data from the "from" cdr to the "to" cdr
+ *
* \param to the cdr to get the goodies
* \param from the cdr to give the goodies
*/
void ast_cdr_merge(struct ast_cdr *to, struct ast_cdr *from);
-/*! \brief Set account code, will generate AMI event */
+/*!
+ * \brief Set account code, will generate AMI event
+ */
int ast_cdr_setaccount(struct ast_channel *chan, const char *account);
-/*! \brief Set the peer account */
+/*!
+ * \brief Set the peer account
+ */
int ast_cdr_setpeeraccount(struct ast_channel *chan, const char *account);
-/*! \brief Set AMA flags for channel */
+/*!
+ * \brief Set AMA flags for channel
+ */
int ast_cdr_setamaflags(struct ast_channel *chan, const char *amaflags);
-/*! \brief Set CDR user field for channel (stored in CDR) */
+/*!
+ * \brief Set CDR user field for channel (stored in CDR)
+ */
int ast_cdr_setuserfield(struct ast_channel *chan, const char *userfield);
-/*! \brief Append to CDR user field for channel (stored in CDR) */
+
+/*!
+ * \brief Append to CDR user field for channel (stored in CDR)
+ */
int ast_cdr_appenduserfield(struct ast_channel *chan, const char *userfield);
-/*! Update CDR on a channel */
+/*!
+ * \brief Update CDR on a channel
+ */
int ast_cdr_update(struct ast_channel *chan);
-
+/*!
+ * \brief Default AMA flags for CDRs
+ */
extern int ast_default_amaflags;
extern char ast_default_accountcode[AST_MAX_ACCOUNT_CODE];
struct ast_cdr *ast_cdr_append(struct ast_cdr *cdr, struct ast_cdr *newcdr);
-/*! \brief Reload the configuration file cdr.conf and start/stop CDR scheduling thread */
+/*!
+ * \brief Reload the configuration file cdr.conf and start/stop CDR scheduling thread
+ */
int ast_cdr_engine_reload(void);
-/*! \brief Load the configuration file cdr.conf and possibly start the CDR scheduling thread */
+/*!
+ * \brief Load the configuration file cdr.conf and possibly start the CDR scheduling thread
+ */
int ast_cdr_engine_init(void);
-/*! Submit any remaining CDRs and prepare for shutdown */
+/*!
+ * \brief Submit any remaining CDRs and prepare for shutdown
+ */
void ast_cdr_engine_term(void);
#endif /* _ASTERISK_CDR_H */
Modified: team/russell/cdr-q/main/asterisk.c
URL: http://svnview.digium.com/svn/asterisk/team/russell/cdr-q/main/asterisk.c?view=diff&rev=248229&r1=248228&r2=248229
==============================================================================
--- team/russell/cdr-q/main/asterisk.c (original)
+++ team/russell/cdr-q/main/asterisk.c Mon Feb 22 01:02:04 2010
@@ -470,7 +470,7 @@
ast_cli(a->fd, " -------------\n");
ast_cli(a->fd, " Manager (AMI): %s\n", check_manager_enabled() ? "Enabled" : "Disabled");
ast_cli(a->fd, " Web Manager (AMI/HTTP): %s\n", check_webmanager_enabled() ? "Enabled" : "Disabled");
- ast_cli(a->fd, " Call data records: %s\n", check_cdr_enabled() ? "Enabled" : "Disabled");
+ ast_cli(a->fd, " Call data records: %s\n", ast_cdr_enabled() ? "Enabled" : "Disabled");
ast_cli(a->fd, " Realtime Architecture (ARA): %s\n", ast_realtime_enabled() ? "Enabled" : "Disabled");
/*! \todo we could check musiconhold, voicemail, smdi, adsi, queues */
Modified: team/russell/cdr-q/main/cdr.c
URL: http://svnview.digium.com/svn/asterisk/team/russell/cdr-q/main/cdr.c?view=diff&rev=248229&r1=248228&r2=248229
==============================================================================
--- team/russell/cdr-q/main/cdr.c (original)
+++ team/russell/cdr-q/main/cdr.c Mon Feb 22 01:02:04 2010
@@ -1,9 +1,10 @@
/*
* Asterisk -- An open source telephony toolkit.
*
- * Copyright (C) 1999 - 2006, Digium, Inc.
+ * Copyright (C) 1999 - 2010, Digium, Inc.
*
* Mark Spencer <markster at digium.com>
+ * Russell Bryant <russell at digium.com>
*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
@@ -16,18 +17,12 @@
* at the top of the source tree.
*/
-/*! \file
- *
- * \brief Call Detail Record API
+/*!
+ * \file
+ * \brief Call Detail Record API
*
* \author Mark Spencer <markster at digium.com>
- *
- * \note Includes code and algorithms from the Zapata library.
- *
- * \note We do a lot of checking here in the CDR code to try to be sure we don't ever let a CDR slip
- * through our fingers somehow. If someone allocates a CDR, it must be completely handled normally
- * or a WARNING shall be logged, so that we can best keep track of any escape condition where the CDR
- * isn't properly generated and posted.
+ * \author Russell Bryant <russell at digium.com>
*/
@@ -35,176 +30,269 @@
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-#include <signal.h>
-
-#include "asterisk/lock.h"
#include "asterisk/channel.h"
#include "asterisk/cdr.h"
-#include "asterisk/callerid.h"
#include "asterisk/manager.h"
#include "asterisk/causes.h"
-#include "asterisk/linkedlists.h"
#include "asterisk/utils.h"
-#include "asterisk/sched.h"
#include "asterisk/config.h"
#include "asterisk/cli.h"
#include "asterisk/stringfields.h"
-
-/*! Default AMA flag for billing records (CDR's) */
-int ast_default_amaflags = AST_CDR_DOCUMENTATION;
+#include "asterisk/linkedlists.h"
+#include "asterisk/taskprocessor.h"
+
+/*!
+ * \internal
+ * \brief Hack to be able to link a CDR struct into a list head
+ */
+struct cdr_wrap {
+ struct ast_cdr *cdr;
+ AST_LIST_ENTRY(cdr_wrap) entry;
+};
+
+/*!
+ * \brief A CDR logging backend
+ */
+struct cdr_backend {
+ AST_DECLARE_STRING_FIELDS(
+ /*!
+ * \brief Unique name for this backend
+ */
+ AST_STRING_FIELD(name);
+ /*!
+ * \brief Description of this backend
+ */
+ AST_STRING_FIELD(desc);
+ );
+ /*!
+ * \brief Callback into the backend for posting CDR
+ */
+ ast_cdr_post_cb post_cb;
+ /*!
+ * \brief A taskprocessor for asynchronously posting CDRs to this backend
+ */
+ struct ast_taskprocessor *tps;
+ /*!
+ * \brief A queue of CDRs not yet posted to this backend
+ */
+ AST_LIST_HEAD_NOLOCK(, cdr_wrap) cdr_q;
+ /*!
+ * \brief Current length of the CDR queue
+ */
+ unsigned int cdr_q_len;
+ /*!
+ * \brief A cache of cdr_wrap objects for use with this backend
+ */
+ struct ao2_container *cdr_wrap_cache;
+ /*!
+ * \brief The last timestamp of a post failure log entry
+ */
+ struct timeval last_error;
+};
+
+/*!
+ * \brief Cache size limit for cdr wrappers
+ */
+static const unsigned int WRAP_CACHE_SIZE_MAX = 10;
+
+/*!
+ * \brief Limit LOG_ERROR messages to 1 per 5 seconds.
+ */
+static const unsigned int ERROR_RATE_LIMIT = 5000;
+
+static struct ao2_container *cdr_backends;
+
+/*!
+ * \brief CDR subsystem configuration parameters
+ */
+static struct cdr_cfg {
+ /*!
+ * \brief Is CDR logging enabled?
+ */
+ unsigned int enabled:1;
+ /*!
+ * \brief Log unanswered outbound call legs?
+ */
+ unsigned int unanswered:1;
+} cdr_cfg;
+
+static const struct cdr_cfg cdr_cfg_default = {
+ .enabled = 1,
+ .unanswered = 0,
+};
+
+static int cdr_sequence;
+
+int ast_default_amaflags;
+static const int DEFAULT_AMAFLAGS = AST_CDR_DOCUMENTATION;
+
char ast_default_accountcode[AST_MAX_ACCOUNT_CODE];
-
-struct ast_cdr_beitem {
- char name[20];
- char desc[80];
- ast_cdrbe be;
- AST_RWLIST_ENTRY(ast_cdr_beitem) list;
-};
-
-static AST_RWLIST_HEAD_STATIC(be_list, ast_cdr_beitem);
-
-struct ast_cdr_batch_item {
- struct ast_cdr *cdr;
- struct ast_cdr_batch_item *next;
-};
-
-static struct ast_cdr_batch {
- int size;
- struct ast_cdr_batch_item *head;
- struct ast_cdr_batch_item *tail;
-} *batch = NULL;
-
-
-static int cdr_sequence = 0;
-
-static int cdr_seq_inc(struct ast_cdr *cdr);
-
-static struct sched_context *sched;
-static int cdr_sched = -1;
-static pthread_t cdr_thread = AST_PTHREADT_NULL;
-
-#define BATCH_SIZE_DEFAULT 100
-#define BATCH_TIME_DEFAULT 300
-#define BATCH_SCHEDULER_ONLY_DEFAULT 0
-#define BATCH_SAFE_SHUTDOWN_DEFAULT 1
-
-static int enabled; /*! Is the CDR subsystem enabled ? */
-static int unanswered;
-static int batchmode;
-static int batchsize;
-static int batchtime;
-static int batchscheduleronly;
-static int batchsafeshutdown;
-
-AST_MUTEX_DEFINE_STATIC(cdr_batch_lock);
-
-/* these are used to wake up the CDR thread when there's work to do */
-AST_MUTEX_DEFINE_STATIC(cdr_pending_lock);
-static ast_cond_t cdr_pending_cond;
-
-int check_cdr_enabled()
-{
- return enabled;
-}
-
-/*! Register a CDR driver. Each registered CDR driver generates a CDR
- \return 0 on success, -1 on failure
-*/
-int ast_cdr_register(const char *name, const char *desc, ast_cdrbe be)
-{
- struct ast_cdr_beitem *i = NULL;
-
- if (!name)
+static const char DEFAULT_ACCOUNTCODE[] = "";
+
+int ast_cdr_enabled(void)
+{
+ return cdr_cfg.enabled;
+}
+
+/*!
+ * \internal
+ * \brief Search for a CDR backend by name
+ *
+ * \arg name the name of a backend to search for
+ *
+ * \return an astobj2 reference to a cdr_backend, or NULL if not found
+ */
+static struct cdr_backend *find_cdr_backend(const char *name)
+{
+ struct cdr_backend tmp = {
+ .name = name,
+ };
+
+ return ao2_find(cdr_backends, &tmp, OBJ_POINTER);
+}
+
+static void cdr_backend_destructor(void *obj)
+{
+ struct cdr_backend *backend = obj;
+
+ if (backend->tps) {
+ backend->tps = ast_taskprocessor_unreference(backend->tps);
+ }
+
+ if (backend->cdr_wrap_cache) {
+ ao2_t_ref(backend->cdr_wrap_cache, -1, "Bye-Bye cdr_wrap cache");
+ backend->cdr_wrap_cache = NULL;
+ }
+
+ ast_string_field_free_memory(backend);
+}
+
+int ast_cdr_register(const char *name, const char *desc, ast_cdr_post_cb post_cb)
+{
+ struct cdr_backend *backend;
+ char tps_name[128];
+ int res = 0;
+
+ if (ast_strlen_zero(name)) {
+ ast_log(LOG_WARNING, "Not registering a CDR backend with no name\n");
return -1;
-
- if (!be) {
- ast_log(LOG_WARNING, "CDR engine '%s' lacks backend\n", name);
+ }
+
+ if (ast_strlen_zero(desc)) {
+ ast_log(LOG_WARNING, "Not registering CDR backend '%s' with no description\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;
- }
- }
-
- if (!(i = ast_calloc(1, sizeof(*i))))
+ if (!post_cb) {
+ ast_log(LOG_WARNING, "Not registering CDR backend '%s' with no callback\n", name);
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);
-
- return 0;
-}
-
-/*! unregister a CDR driver */
+ }
+
+ if ((backend = find_cdr_backend(name))) {
+ ao2_t_ref(backend, -1, "Found a backend that already existed");
+ backend = NULL;
+ ast_log(LOG_WARNING, "CDR backend named '%s' already exists\n", name);
+ return -1;
+ }
+
+ if (!(backend = ao2_alloc(sizeof(*backend), cdr_backend_destructor))) {
+ return -1;
+ }
+
+ snprintf(tps_name, sizeof(tps_name), "CDR-BACKEND-%s", name);
+
+ if (!(backend->tps = ast_taskprocessor_get(tps_name, TPS_REF_DEFAULT))) {
+ res = -1;
+ goto return_cleanup;
+ }
+
+ if (ast_string_field_init(backend, 128)) {
+ res = -1;
+ goto return_cleanup;
+ }
+
+ if (!(backend->cdr_wrap_cache = ao2_container_alloc(1, NULL, NULL))) {
+ res = -1;
+ goto return_cleanup;
+ }
+
+ ast_string_field_set(backend, name, name);
+ ast_string_field_set(backend, desc, desc);
+ backend->post_cb = post_cb;
+
+ ao2_link(cdr_backends, backend);
+
+return_cleanup:
+ ao2_t_ref(backend, -1, "Done registering a backend");
+
+ return res;
+}
+
void ast_cdr_unregister(const char *name)
{
- struct ast_cdr_beitem *i = NULL;
-
- AST_RWLIST_WRLOCK(&be_list);
- AST_RWLIST_TRAVERSE_SAFE_BEGIN(&be_list, i, list) {
- if (!strcasecmp(name, i->name)) {
- AST_RWLIST_REMOVE_CURRENT(list);
- break;
- }
- }
- AST_RWLIST_TRAVERSE_SAFE_END;
- AST_RWLIST_UNLOCK(&be_list);
-
- if (i) {
- ast_verb(2, "Unregistered '%s' CDR backend\n", name);
- ast_free(i);
- }
+ struct cdr_backend *backend;
+
+ if (!(backend = find_cdr_backend(name))) {
+ ast_log(LOG_WARNING, "CDR backend '%s' not found to unregister\n", name);
+ return;
+ }
+
+ ao2_unlink(cdr_backends, backend);
+
+ ao2_t_ref(backend, -1, "backend unregister");
+
+ ast_verb(2, "Unregistered '%s' CDR backend\n", name);
}
int ast_cdr_isset_unanswered(void)
{
- return unanswered;
-}
-
-struct ast_cdr *ast_cdr_dup_unique(struct ast_cdr *cdr)
+ return cdr_cfg.unanswered;
+}
+
+static inline void cdr_seq_inc(struct ast_cdr *cdr)
+{
+ cdr->sequence = ast_atomic_fetchadd_int(&cdr_sequence, +1);
+}
+
+struct ast_cdr *ast_cdr_dup_unique(struct ast_cdr *cdr)
{
struct ast_cdr *newcdr = ast_cdr_dup(cdr);
- if (!newcdr)
+
+ if (!newcdr) {
return NULL;
+ }
cdr_seq_inc(newcdr);
+
return newcdr;
}
-struct ast_cdr *ast_cdr_dup_unique_swap(struct ast_cdr *cdr)
+struct ast_cdr *ast_cdr_dup_unique_swap(struct ast_cdr *cdr)
{
struct ast_cdr *newcdr = ast_cdr_dup(cdr);
- if (!newcdr)
+
+ if (!newcdr) {
return NULL;
+ }
cdr_seq_inc(cdr);
+
return newcdr;
}
-/*! Duplicate a CDR record
- \returns Pointer to new CDR record
-*/
-struct ast_cdr *ast_cdr_dup(struct ast_cdr *cdr)
+struct ast_cdr *ast_cdr_dup(struct ast_cdr *cdr)
{
struct ast_cdr *newcdr;
-
- if (!cdr) /* don't die if we get a null cdr pointer */
+
+ if (!cdr) {
return NULL;
- newcdr = ast_cdr_alloc();
- if (!newcdr)
+ }
+
+ if (!(newcdr = ast_cdr_alloc())) {
return NULL;
-
- memcpy(newcdr, cdr, sizeof(*newcdr));
- /* The varshead is unusable, volatile even, after the memcpy so we take care of that here */
+ }
+
+ *newcdr = *cdr;
memset(&newcdr->varshead, 0, sizeof(newcdr->varshead));
ast_cdr_copy_vars(newcdr, cdr);
newcdr->next = NULL;
@@ -212,17 +300,19 @@
return newcdr;
}
-static const char *ast_cdr_getvar_internal(struct ast_cdr *cdr, const char *name, int recur)
-{
- if (ast_strlen_zero(name))
+static const char *ast_cdr_getvar_internal(struct ast_cdr *cdr, const char *name, int recur)
+{
+ if (ast_strlen_zero(name)) {
return NULL;
+ }
for (; cdr; cdr = recur ? cdr->next : NULL) {
struct ast_var_t *variables;
struct varshead *headp = &cdr->varshead;
AST_LIST_TRAVERSE(headp, variables, entries) {
- if (!strcasecmp(name, ast_var_name(variables)))
+ if (!strcasecmp(name, ast_var_name(variables))) {
return ast_var_value(variables);
+ }
}
}
@@ -231,20 +321,21 @@
static void cdr_get_tv(struct timeval when, const char *fmt, char *buf, int bufsize)
{
- if (fmt == NULL) { /* raw mode */
+ if (!fmt) { /* raw mode */
snprintf(buf, bufsize, "%ld.%06ld", (long)when.tv_sec, (long)when.tv_usec);
- } else {
- if (when.tv_sec) {
- struct ast_tm tm;
-
- ast_localtime(&when, &tm, NULL);
- ast_strftime(buf, bufsize, fmt, &tm);
- }
- }
-}
-
-/*! CDR channel variable retrieval */
-void ast_cdr_getvar(struct ast_cdr *cdr, const char *name, char **ret, char *workspace, int workspacelen, int recur, int raw)
+ return;
+ }
+
+ if (when.tv_sec) {
+ struct ast_tm tm;
+
+ ast_localtime(&when, &tm, NULL);
+ ast_strftime(buf, bufsize, fmt, &tm);
+ }
+}
+
+void ast_cdr_getvar(struct ast_cdr *cdr, const char *name, char **ret,
+ char *workspace, int workspacelen, int recur, int raw)
{
const char *fmt = "%Y-%m-%d %T";
const char *varbuf;
@@ -253,7 +344,7 @@
return;
*ret = NULL;
- /* special vars (the ones from the struct ast_cdr when requested by name)
+ /* special vars (the ones from the struct ast_cdr when requested by name)
I'd almost say we should convert all the stringed vals to vars */
if (!strcasecmp(name, "clid"))
@@ -311,24 +402,27 @@
else
workspace[0] = '\0';
- if (!ast_strlen_zero(workspace))
+ if (!ast_strlen_zero(workspace)) {
*ret = workspace;
-}
-
-/* readonly cdr variables */
-static const char * const cdr_readonly_vars[] = { "clid", "src", "dst", "dcontext", "channel", "dstchannel",
- "lastapp", "lastdata", "start", "answer", "end", "duration",
- "billsec", "disposition", "amaflags", "accountcode", "uniqueid", "linkedid",
- "userfield", "sequence", NULL };
-/*! Set a CDR channel variable
- \note You can't set the CDR variables that belong to the actual CDR record, like "billsec".
-*/
-int ast_cdr_setvar(struct ast_cdr *cdr, const char *name, const char *value, int recur)
+ }
+}
+
+/*!
+ * \brief Read-only CDR variables
+ */
+static const char * const cdr_readonly_vars[] = {
+ "clid", "src", "dst", "dcontext", "channel", "dstchannel",
+ "lastapp", "lastdata", "start", "answer", "end", "duration",
+ "billsec", "disposition", "amaflags", "accountcode", "uniqueid",
+ "linkedid", "userfield", "sequence", NULL
+};
+
+int ast_cdr_setvar(struct ast_cdr *cdr, const char *name, const char *value, int recur)
{
struct ast_var_t *newvariable;
struct varshead *headp;
int x;
-
+
for (x = 0; cdr_readonly_vars[x]; x++) {
if (!strcasecmp(name, cdr_readonly_vars[x])) {
ast_log(LOG_ERROR, "Attempt to set the '%s' read-only variable!.\n", name);
@@ -354,7 +448,7 @@
}
}
AST_LIST_TRAVERSE_SAFE_END;
-
+
if (value) {
newvariable = ast_var_assign(name, value);
AST_LIST_INSERT_HEAD(headp, newvariable, entries);
@@ -371,8 +465,9 @@
const char *var, *val;
int x = 0;
- if (!to_cdr || !from_cdr) /* don't die if one of the pointers is null */
+ if (!to_cdr || !from_cdr) {
return 0;
+ }
headpa = &from_cdr->varshead;
headpb = &to_cdr->varshead;
@@ -390,7 +485,7 @@
return x;
}
-int ast_cdr_serialize_variables(struct ast_cdr *cdr, struct ast_str **buf, char delim, char sep, int recur)
+int ast_cdr_serialize_variables(struct ast_cdr *cdr, struct ast_str **buf, char delim, char sep, int recur)
{
struct ast_var_t *variables;
const char *var, *val;
@@ -401,33 +496,38 @@
ast_str_reset(*buf);
for (; cdr; cdr = recur ? cdr->next : NULL) {
- if (++x > 1)
+ if (++x > 1) {
ast_str_append(buf, 0, "\n");
+ }
AST_LIST_TRAVERSE(&cdr->varshead, variables, entries) {
if (variables &&
(var = ast_var_name(variables)) && (val = ast_var_value(variables)) &&
!ast_strlen_zero(var) && !ast_strlen_zero(val)) {
if (ast_str_append(buf, 0, "level %d: %s%c%s%c", x, var, delim, val, sep) < 0) {
- ast_log(LOG_ERROR, "Data Buffer Size Exceeded!\n");
- break;
- } else
+ ast_log(LOG_ERROR, "Data Buffer Size Exceeded!\n");
+ break;
+ } else {
total++;
- } else
+ }
+ } else {
break;
+ }
}
for (i = 0; cdr_readonly_vars[i]; i++) {
workspace[0] = 0; /* null out the workspace, because the cdr_get_tv() won't write anything if time is NULL, so you get old vals */
ast_cdr_getvar(cdr, cdr_readonly_vars[i], &tmp, workspace, sizeof(workspace), 0, 0);
- if (!tmp)
+ if (!tmp) {
continue;
-
+ }
+
if (ast_str_append(buf, 0, "level %d: %s%c%s%c", x, cdr_readonly_vars[i], delim, tmp, sep) < 0) {
ast_log(LOG_ERROR, "Data Buffer Size Exceeded!\n");
break;
- } else
+ } else {
total++;
+ }
}
}
@@ -437,56 +537,50 @@
void ast_cdr_free_vars(struct ast_cdr *cdr, int recur)
{
-
- /* clear variables */
for (; cdr; cdr = recur ? cdr->next : NULL) {
struct ast_var_t *vardata;
struct varshead *headp = &cdr->varshead;
- while ((vardata = AST_LIST_REMOVE_HEAD(headp, entries)))
+ while ((vardata = AST_LIST_REMOVE_HEAD(headp, entries))) {
ast_var_delete(vardata);
- }
-}
-
-/*! \brief print a warning if cdr already posted */
+ }
+ }
+}
+
+/*!
+ * \internal
+ * \brief print a warning if CDR already posted
+ */
static void check_post(struct ast_cdr *cdr)
{
- if (!cdr)
+ if (!cdr) {
return;
- if (ast_test_flag(cdr, AST_CDR_FLAG_POSTED))
- ast_log(LOG_NOTICE, "CDR on channel '%s' already posted\n", S_OR(cdr->channel, "<unknown>"));
-}
-
-void ast_cdr_free(struct ast_cdr *cdr)
-{
-
- while (cdr) {
- struct ast_cdr *next = cdr->next;
-
- ast_cdr_free_vars(cdr, 0);
- ast_free(cdr);
- cdr = next;
- }
-}
-
-/*! \brief the same as a cdr_free call, only with no checks; just get rid of it */
+ }
+
+ if (ast_test_flag(cdr, AST_CDR_FLAG_POSTED)) {
+ ast_log(LOG_NOTICE, "CDR on channel '%s' already posted\n",
+ S_OR(cdr->channel, "<unknown>"));
+ }
+}
+
void ast_cdr_discard(struct ast_cdr *cdr)
{
- while (cdr) {
- struct ast_cdr *next = cdr->next;
-
- ast_cdr_free_vars(cdr, 0);
- ast_free(cdr);
- cdr = next;
- }
+ struct ast_cdr *next = cdr->next;
+
+ for (; cdr; cdr = next, next = cdr ? cdr->next : NULL) {
+ ao2_t_ref(cdr, -1, "discarding cdr");
+ }
+}
+
+static void ast_cdr_destructor(void *obj)
+{
+ struct ast_cdr *cdr = obj;
+
+ ast_cdr_free_vars(cdr, 0);
}
struct ast_cdr *ast_cdr_alloc(void)
{
- struct ast_cdr *x;
- x = ast_calloc(1, sizeof(*x));
- if (!x)
- ast_log(LOG_ERROR,"Allocation Failure for a CDR!\n");
- return x;
+ return ao2_alloc(sizeof(struct ast_cdr), ast_cdr_destructor);
}
static void cdr_merge_vars(struct ast_cdr *to, struct ast_cdr *from)
@@ -494,6 +588,7 @@
struct ast_var_t *variablesfrom,*variablesto;
struct varshead *headpfrom = &to->varshead;
struct varshead *headpto = &from->varshead;
+
AST_LIST_TRAVERSE_SAFE_BEGIN(headpfrom, variablesfrom, entries) {
/* for every var in from, stick it in to */
const char *fromvarname, *fromvarval;
@@ -504,9 +599,8 @@
/* now, quick see if that var is in the 'to' cdr already */
AST_LIST_TRAVERSE(headpto, variablesto, entries) {
-
/* now, quick see if that var is in the 'to' cdr already */
- if ( strcasecmp(fromvarname, ast_var_name(variablesto)) == 0 ) {
+ if (!strcasecmp(fromvarname, ast_var_name(variablesto))) {
tovarname = ast_var_name(variablesto);
tovarval = ast_var_value(variablesto);
break;
@@ -515,8 +609,9 @@
if (tovarname && strcasecmp(fromvarval,tovarval) != 0) { /* this message here to see how irritating the userbase finds it */
ast_log(LOG_NOTICE, "Merging CDR's: variable %s value %s dropped in favor of value %s\n", tovarname, fromvarval, tovarval);
continue;
- } else if (tovarname && strcasecmp(fromvarval,tovarval) == 0) /* if they are the same, the job is done */
+ } else if (tovarname && !strcasecmp(fromvarval,tovarval)) { /* if they are the same, the job is done */
continue;
+ }
/* rip this var out of the from cdr, and stick it in the to cdr */
AST_LIST_MOVE_CURRENT(headpto, entries);
@@ -530,9 +625,10 @@
struct ast_cdr *lto = NULL;
struct ast_cdr *lfrom = NULL;
int discard_from = 0;
-
- if (!to || !from)
+
+ if (!to || !from) {
return;
+ }
/* don't merge into locked CDR's -- it's bad business */
if (ast_test_flag(to, AST_CDR_FLAG_LOCKED)) {
@@ -541,7 +637,7 @@
lto = to;
to = to->next;
}
-
+
if (ast_test_flag(to, AST_CDR_FLAG_LOCKED)) {
ast_log(LOG_WARNING, "Merging into locked CDR... no choice.");
to = zcdr; /* safety-- if all there are is locked CDR's, then.... ?? */
@@ -557,9 +653,10 @@
lto->next = from;
lfrom = from;
while (lfrom && lfrom->next) {
- if (!lfrom->next->next)
+ if (!lfrom->next->next) {
llfrom = lfrom;
- lfrom = lfrom->next;
+ }
+ lfrom = lfrom->next;
}
/* rip off the last entry and put a copy of the to at the end */
llfrom->next = to;
@@ -567,25 +664,27 @@
} else {
/* save copy of the current *to cdr */
struct ast_cdr tcdr;
- memcpy(&tcdr, to, sizeof(tcdr));
+ tcdr = *to;
/* copy in the locked from cdr */
- memcpy(to, from, sizeof(*to));
+ *to = *from;
lfrom = from;
while (lfrom && lfrom->next) {
- if (!lfrom->next->next)
+ if (!lfrom->next->next) {
llfrom = lfrom;
- lfrom = lfrom->next;
+ }
+ lfrom = lfrom->next;
}
from->next = NULL;
/* rip off the last entry and put a copy of the to at the end */
- if (llfrom == from)
+ if (llfrom == from) {
to = to->next = ast_cdr_dup(&tcdr);
- else
+ } else {
to = llfrom->next = ast_cdr_dup(&tcdr);
+ }
from = lfrom;
}
}
-
+
if (!ast_tvzero(from->start)) {
if (!ast_tvzero(to->start)) {
if (ast_tvcmp(to->start, from->start) > 0 ) {
@@ -681,16 +780,21 @@
/* flags, varsead, ? */
cdr_merge_vars(from, to);
- if (ast_test_flag(from, AST_CDR_FLAG_KEEP_VARS))
+ if (ast_test_flag(from, AST_CDR_FLAG_KEEP_VARS)) {
ast_set_flag(to, AST_CDR_FLAG_KEEP_VARS);
- if (ast_test_flag(from, AST_CDR_FLAG_POSTED))
+ }
+ if (ast_test_flag(from, AST_CDR_FLAG_POSTED)) {
ast_set_flag(to, AST_CDR_FLAG_POSTED);
- if (ast_test_flag(from, AST_CDR_FLAG_LOCKED))
+ }
+ if (ast_test_flag(from, AST_CDR_FLAG_LOCKED)) {
ast_set_flag(to, AST_CDR_FLAG_LOCKED);
- if (ast_test_flag(from, AST_CDR_FLAG_CHILD))
+ }
+ if (ast_test_flag(from, AST_CDR_FLAG_CHILD)) {
ast_set_flag(to, AST_CDR_FLAG_CHILD);
- if (ast_test_flag(from, AST_CDR_FLAG_POST_DISABLED))
+ }
+ if (ast_test_flag(from, AST_CDR_FLAG_POST_DISABLED)) {
ast_set_flag(to, AST_CDR_FLAG_POST_DISABLED);
+ }
/* last, but not least, we need to merge any forked CDRs to the 'to' cdr */
while (from->next) {
@@ -701,13 +805,14 @@
/* zcdr is now ripped from the current list; */
ast_cdr_append(to, zcdr);
}
- if (discard_from)
+ if (discard_from) {
ast_cdr_discard(from);
+ }
}
void ast_cdr_start(struct ast_cdr *cdr)
{
- char *chan;
+ char *chan;
for (; cdr; cdr = cdr->next) {
[... 1316 lines stripped ...]
More information about the asterisk-commits
mailing list