Migrate a large number of AMI events over to Stasis-Core
[asterisk/asterisk.git] / main / cdr.c
index fdfaad1..a056067 100644 (file)
  * isn't properly generated and posted.
  */
 
+/*! \li \ref cdr.c uses the configuration file \ref cdr.conf
+ * \addtogroup configuration_file Configuration Files
+ */
+
+/*!
+ * \page cdr.conf cdr.conf
+ * \verbinclude cdr.conf.sample
+ */
+
+/*** MODULEINFO
+       <support_level>core</support_level>
+ ***/
 
 #include "asterisk.h"
 
@@ -51,6 +63,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/stringfields.h"
 #include "asterisk/data.h"
 
+/*** DOCUMENTATION
+ ***/
+
 /*! Default AMA flag for billing records (CDR's) */
 int ast_default_amaflags = AST_CDR_DOCUMENTATION;
 char ast_default_accountcode[AST_MAX_ACCOUNT_CODE];
@@ -108,6 +123,8 @@ static const int BATCH_SCHEDULER_ONLY_DEFAULT = 0;
 static int batchsafeshutdown;
 static const int BATCH_SAFE_SHUTDOWN_DEFAULT = 1;
 
+AST_MUTEX_DEFINE_STATIC(cdr_sched_lock);
+
 AST_MUTEX_DEFINE_STATIC(cdr_batch_lock);
 
 /* these are used to wake up the CDR thread when there's work to do */
@@ -297,11 +314,11 @@ void ast_cdr_getvar(struct ast_cdr *cdr, const char *name, char **ret, char *wor
                cdr_get_tv(cdr->answer, raw ? NULL : fmt, workspace, workspacelen);
        else if (!strcasecmp(name, "end"))
                cdr_get_tv(cdr->end, raw ? NULL : fmt, workspace, workspacelen);
-       else if (!strcasecmp(name, "duration"))
-               snprintf(workspace, workspacelen, "%ld", cdr->duration ? cdr->duration : (long)ast_tvdiff_ms(ast_tvnow(), cdr->start) / 1000);
-       else if (!strcasecmp(name, "billsec"))
-               snprintf(workspace, workspacelen, "%ld", cdr->billsec || cdr->answer.tv_sec == 0 ? cdr->billsec : (long)ast_tvdiff_ms(ast_tvnow(), cdr->answer) / 1000);
-       else if (!strcasecmp(name, "disposition")) {
+       else if (!strcasecmp(name, "duration")) {
+               snprintf(workspace, workspacelen, "%ld", cdr->end.tv_sec != 0 ? cdr->duration : (long)ast_tvdiff_ms(ast_tvnow(), cdr->start) / 1000);
+       } else if (!strcasecmp(name, "billsec")) {
+               snprintf(workspace, workspacelen, "%ld", (cdr->billsec || !ast_tvzero(cdr->end) || ast_tvzero(cdr->answer)) ? cdr->billsec : (long)ast_tvdiff_ms(ast_tvnow(), cdr->answer) / 1000);     
+       } else if (!strcasecmp(name, "disposition")) {
                if (raw) {
                        snprintf(workspace, workspacelen, "%ld", cdr->disposition);
                } else {
@@ -562,7 +579,7 @@ void ast_cdr_merge(struct ast_cdr *to, struct ast_cdr *from)
                }
 
                if (ast_test_flag(to, AST_CDR_FLAG_LOCKED)) {
-                       ast_log(LOG_WARNING, "Merging into locked CDR... no choice.");
+                       ast_log(LOG_WARNING, "Merging into locked CDR... no choice.\n");
                        to = zcdr; /* safety-- if all there are is locked CDR's, then.... ?? */
                        lto = NULL;
                }
@@ -581,7 +598,9 @@ void ast_cdr_merge(struct ast_cdr *to, struct ast_cdr *from)
                                lfrom = lfrom->next;
                        }
                        /* rip off the last entry and put a copy of the to at the end */
-                       llfrom->next = to;
+                       if (llfrom) {
+                               llfrom->next = to;
+                       }
                        from = lfrom;
                } else {
                        /* save copy of the current *to cdr */
@@ -597,10 +616,11 @@ void ast_cdr_merge(struct ast_cdr *to, struct ast_cdr *from)
                        }
                        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 if (llfrom) {
                                to = llfrom->next = ast_cdr_dup(&tcdr);
+                       }
                        from = lfrom;
                }
        }
@@ -893,18 +913,18 @@ static void set_one_cid(struct ast_cdr *cdr, struct ast_channel *c)
        }
 
        /* Grab source from ANI or normal Caller*ID */
-       num = S_COR(c->caller.ani.number.valid, c->caller.ani.number.str,
-               S_COR(c->caller.id.number.valid, c->caller.id.number.str, NULL));
+       num = S_COR(ast_channel_caller(c)->ani.number.valid, ast_channel_caller(c)->ani.number.str,
+               S_COR(ast_channel_caller(c)->id.number.valid, ast_channel_caller(c)->id.number.str, NULL));
        ast_callerid_merge(cdr->clid, sizeof(cdr->clid),
-               S_COR(c->caller.id.name.valid, c->caller.id.name.str, NULL), num, "");
+               S_COR(ast_channel_caller(c)->id.name.valid, ast_channel_caller(c)->id.name.str, NULL), num, "");
        ast_copy_string(cdr->src, S_OR(num, ""), sizeof(cdr->src));
-       ast_cdr_setvar(cdr, "dnid", S_OR(c->dialed.number.str, ""), 0);
+       ast_cdr_setvar(cdr, "dnid", S_OR(ast_channel_dialed(c)->number.str, ""), 0);
 
-       if (c->caller.id.subaddress.valid) {
-               ast_cdr_setvar(cdr, "callingsubaddr", S_OR(c->caller.id.subaddress.str, ""), 0);
+       if (ast_channel_caller(c)->id.subaddress.valid) {
+               ast_cdr_setvar(cdr, "callingsubaddr", S_OR(ast_channel_caller(c)->id.subaddress.str, ""), 0);
        }
-       if (c->dialed.subaddress.valid) {
-               ast_cdr_setvar(cdr, "calledsubaddr", S_OR(c->dialed.subaddress.str, ""), 0);
+       if (ast_channel_dialed(c)->subaddress.valid) {
+               ast_cdr_setvar(cdr, "calledsubaddr", S_OR(ast_channel_dialed(c)->subaddress.str, ""), 0);
        }
 }
 
@@ -926,21 +946,21 @@ int ast_cdr_init(struct ast_cdr *cdr, struct ast_channel *c)
 {
        for ( ; cdr ; cdr = cdr->next) {
                if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
-                       ast_copy_string(cdr->channel, c->name, sizeof(cdr->channel));
+                       ast_copy_string(cdr->channel, ast_channel_name(c), sizeof(cdr->channel));
                        set_one_cid(cdr, c);
                        cdr_seq_inc(cdr);
 
-                       cdr->disposition = (c->_state == AST_STATE_UP) ?  AST_CDR_ANSWERED : AST_CDR_NOANSWER;
-                       cdr->amaflags = c->amaflags ? c->amaflags :  ast_default_amaflags;
-                       ast_copy_string(cdr->accountcode, c->accountcode, sizeof(cdr->accountcode));
-                       ast_copy_string(cdr->peeraccount, c->peeraccount, sizeof(cdr->peeraccount));
+                       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;
+                       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 */
-                       ast_copy_string(cdr->dst, S_OR(c->macroexten,c->exten), sizeof(cdr->dst));
-                       ast_copy_string(cdr->dcontext, S_OR(c->macrocontext,c->context), sizeof(cdr->dcontext));
+                       ast_copy_string(cdr->dst, S_OR(ast_channel_macroexten(c),ast_channel_exten(c)), sizeof(cdr->dst));
+                       ast_copy_string(cdr->dcontext, S_OR(ast_channel_macrocontext(c),ast_channel_context(c)), sizeof(cdr->dcontext));
                        /* Unique call identifier */
-                       ast_copy_string(cdr->uniqueid, c->uniqueid, sizeof(cdr->uniqueid));
+                       ast_copy_string(cdr->uniqueid, ast_channel_uniqueid(c), sizeof(cdr->uniqueid));
                        /* Linked call identifier */
-                       ast_copy_string(cdr->linkedid, c->linkedid, sizeof(cdr->linkedid));
+                       ast_copy_string(cdr->linkedid, ast_channel_linkedid(c), sizeof(cdr->linkedid));
                }
        }
        return 0;
@@ -1003,7 +1023,10 @@ char *ast_cdr_disp2str(int disposition)
        return "UNKNOWN";
 }
 
-/*! Converts AMA flag to printable string */
+/*! Converts AMA flag to printable string 
+ *
+ * \param flag, flags
+ */
 char *ast_cdr_flags2str(int flag)
 {
        switch (flag) {
@@ -1019,52 +1042,60 @@ char *ast_cdr_flags2str(int flag)
 
 int ast_cdr_setaccount(struct ast_channel *chan, const char *account)
 {
-       struct ast_cdr *cdr = chan->cdr;
+       struct ast_cdr *cdr = ast_channel_cdr(chan);
        const char *old_acct = "";
 
-       if (!ast_strlen_zero(chan->accountcode)) {
-               old_acct = ast_strdupa(chan->accountcode);
+       if (!ast_strlen_zero(ast_channel_accountcode(chan))) {
+               old_acct = ast_strdupa(ast_channel_accountcode(chan));
        }
 
-       ast_string_field_set(chan, accountcode, account);
+       ast_channel_accountcode_set(chan, account);
        for ( ; cdr ; cdr = cdr->next) {
                if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
-                       ast_copy_string(cdr->accountcode, chan->accountcode, sizeof(cdr->accountcode));
+                       ast_copy_string(cdr->accountcode, ast_channel_accountcode(chan), sizeof(cdr->accountcode));
                }
        }
-
+       /*** DOCUMENTATION
+               <managerEventInstance>
+                       <synopsis>Raised when a CDR's AccountCode is changed.</synopsis>
+               </managerEventInstance>
+       ***/
        ast_manager_event(chan, EVENT_FLAG_CALL, "NewAccountCode",
                        "Channel: %s\r\n"
                        "Uniqueid: %s\r\n"
                        "AccountCode: %s\r\n"
                        "OldAccountCode: %s\r\n",
-                       chan->name, chan->uniqueid, chan->accountcode, old_acct);
+                       ast_channel_name(chan), ast_channel_uniqueid(chan), ast_channel_accountcode(chan), old_acct);
 
        return 0;
 }
 
 int ast_cdr_setpeeraccount(struct ast_channel *chan, const char *account)
 {
-       struct ast_cdr *cdr = chan->cdr;
+       struct ast_cdr *cdr = ast_channel_cdr(chan);
        const char *old_acct = "";
 
-       if (!ast_strlen_zero(chan->peeraccount)) {
-               old_acct = ast_strdupa(chan->peeraccount);
+       if (!ast_strlen_zero(ast_channel_peeraccount(chan))) {
+               old_acct = ast_strdupa(ast_channel_peeraccount(chan));
        }
 
-       ast_string_field_set(chan, peeraccount, account);
+       ast_channel_peeraccount_set(chan, account);
        for ( ; cdr ; cdr = cdr->next) {
                if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
-                       ast_copy_string(cdr->peeraccount, chan->peeraccount, sizeof(cdr->peeraccount));
+                       ast_copy_string(cdr->peeraccount, ast_channel_peeraccount(chan), sizeof(cdr->peeraccount));
                }
        }
-
+       /*** DOCUMENTATION
+               <managerEventInstance>
+                       <synopsis>Raised when a CDR's PeerAccount is changed.</synopsis>
+               </managerEventInstance>
+       ***/
        ast_manager_event(chan, EVENT_FLAG_CALL, "NewPeerAccount",
                        "Channel: %s\r\n"
                        "Uniqueid: %s\r\n"
                        "PeerAccount: %s\r\n"
                        "OldPeerAccount: %s\r\n",
-                       chan->name, chan->uniqueid, chan->peeraccount, old_acct);
+                       ast_channel_name(chan), ast_channel_uniqueid(chan), ast_channel_peeraccount(chan), old_acct);
 
        return 0;
 }
@@ -1074,7 +1105,7 @@ int ast_cdr_setamaflags(struct ast_channel *chan, const char *flag)
        struct ast_cdr *cdr;
        int newflag = ast_cdr_amaflags2int(flag);
        if (newflag) {
-               for (cdr = chan->cdr; cdr; cdr = cdr->next) {
+               for (cdr = ast_channel_cdr(chan); cdr; cdr = cdr->next) {
                        if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
                                cdr->amaflags = newflag;
                        }
@@ -1086,7 +1117,7 @@ int ast_cdr_setamaflags(struct ast_channel *chan, const char *flag)
 
 int ast_cdr_setuserfield(struct ast_channel *chan, const char *userfield)
 {
-       struct ast_cdr *cdr = chan->cdr;
+       struct ast_cdr *cdr = ast_channel_cdr(chan);
 
        for ( ; cdr ; cdr = cdr->next) {
                if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
@@ -1098,7 +1129,7 @@ int ast_cdr_setuserfield(struct ast_channel *chan, const char *userfield)
 
 int ast_cdr_appenduserfield(struct ast_channel *chan, const char *userfield)
 {
-       struct ast_cdr *cdr = chan->cdr;
+       struct ast_cdr *cdr = ast_channel_cdr(chan);
 
        for ( ; cdr ; cdr = cdr->next) {
                int len = strlen(cdr->userfield);
@@ -1112,20 +1143,20 @@ int ast_cdr_appenduserfield(struct ast_channel *chan, const char *userfield)
 
 int ast_cdr_update(struct ast_channel *c)
 {
-       struct ast_cdr *cdr = c->cdr;
+       struct ast_cdr *cdr = ast_channel_cdr(c);
 
        for ( ; cdr ; cdr = cdr->next) {
                if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
                        set_one_cid(cdr, c);
 
                        /* Copy account code et-al */
-                       ast_copy_string(cdr->accountcode, c->accountcode, sizeof(cdr->accountcode));
-                       ast_copy_string(cdr->peeraccount, c->peeraccount, sizeof(cdr->peeraccount));
-                       ast_copy_string(cdr->linkedid, c->linkedid, sizeof(cdr->linkedid));
+                       ast_copy_string(cdr->accountcode, ast_channel_accountcode(c), sizeof(cdr->accountcode));
+                       ast_copy_string(cdr->peeraccount, ast_channel_peeraccount(c), sizeof(cdr->peeraccount));
+                       ast_copy_string(cdr->linkedid, ast_channel_linkedid(c), sizeof(cdr->linkedid));
 
                        /* Destination information */ /* XXX privilege macro* ? */
-                       ast_copy_string(cdr->dst, S_OR(c->macroexten, c->exten), sizeof(cdr->dst));
-                       ast_copy_string(cdr->dcontext, S_OR(c->macrocontext, c->context), sizeof(cdr->dcontext));
+                       ast_copy_string(cdr->dst, S_OR(ast_channel_macroexten(c), ast_channel_exten(c)), sizeof(cdr->dst));
+                       ast_copy_string(cdr->dcontext, S_OR(ast_channel_macrocontext(c), ast_channel_context(c)), sizeof(cdr->dcontext));
                }
        }
 
@@ -1331,17 +1362,24 @@ static int submit_scheduled_batch(const void *data)
 {
        ast_cdr_submit_batch(0);
        /* manually reschedule from this point in time */
+       ast_mutex_lock(&cdr_sched_lock);
        cdr_sched = ast_sched_add(sched, batchtime * 1000, submit_scheduled_batch, NULL);
+       ast_mutex_unlock(&cdr_sched_lock);
        /* returning zero so the scheduler does not automatically reschedule */
        return 0;
 }
 
+/*! Do not hold the batch lock while calling this function */
 static void submit_unscheduled_batch(void)
 {
+       /* Prevent two deletes from happening at the same time */
+       ast_mutex_lock(&cdr_sched_lock);
        /* this is okay since we are not being called from within the scheduler */
        AST_SCHED_DEL(sched, cdr_sched);
        /* schedule the submission to occur ASAP (1 ms) */
        cdr_sched = ast_sched_add(sched, 1, submit_scheduled_batch, NULL);
+       ast_mutex_unlock(&cdr_sched_lock);
+
        /* signal the do_cdr thread to wakeup early and do some work (that lazy thread ;) */
        ast_mutex_lock(&cdr_pending_lock);
        ast_cond_signal(&cdr_pending_cond);
@@ -1352,6 +1390,7 @@ void ast_cdr_detach(struct ast_cdr *cdr)
 {
        struct ast_cdr_batch_item *newtail;
        int curr;
+       int submit_batch = 0;
 
        if (!cdr)
                return;
@@ -1395,11 +1434,17 @@ void ast_cdr_detach(struct ast_cdr *cdr)
        newtail->cdr = cdr;
        batch->tail = newtail;
        curr = batch->size++;
-       ast_mutex_unlock(&cdr_batch_lock);
 
        /* if we have enough stuff to post, then do it */
-       if (curr >= (batchsize - 1))
+       if (curr >= (batchsize - 1)) {
+               submit_batch = 1;
+       }
+       ast_mutex_unlock(&cdr_batch_lock);
+
+       /* Don't call submit_unscheduled_batch with the cdr_batch_lock held */
+       if (submit_batch) {
                submit_unscheduled_batch();
+       }
 }
 
 static void *do_cdr(void *data)
@@ -1511,7 +1556,7 @@ static char *handle_cli_submit(struct ast_cli_entry *e, int cmd, struct ast_cli_
 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 int do_reload(int reload)
+static void do_reload(int reload)
 {
        struct ast_config *config;
        struct ast_variable *v;
@@ -1519,11 +1564,10 @@ static int do_reload(int reload)
        int cfg_time;
        int was_enabled;
        int was_batchmode;
-       int res = 0;
        struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
 
        if ((config = ast_config_load2("cdr.conf", "cdr", config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
-               return 0;
+               return;
        }
 
        ast_mutex_lock(&cdr_batch_lock);
@@ -1542,11 +1586,13 @@ static int do_reload(int reload)
 
        if (config == CONFIG_STATUS_FILEMISSING || config == CONFIG_STATUS_FILEINVALID) {
                ast_mutex_unlock(&cdr_batch_lock);
-               return 0;
+               return;
        }
 
        /* don't run the next scheduled CDR posting while reloading */
+       ast_mutex_lock(&cdr_sched_lock);
        AST_SCHED_DEL(sched, cdr_sched);
+       ast_mutex_unlock(&cdr_sched_lock);
 
        for (v = ast_variable_browse(config, "general"); v; v = v->next) {
                if (!strcasecmp(v->name, "enable")) {
@@ -1587,7 +1633,9 @@ static int do_reload(int reload)
        if (enabled && !batchmode) {
                ast_log(LOG_NOTICE, "CDR simple logging enabled.\n");
        } else if (enabled && batchmode) {
+               ast_mutex_lock(&cdr_sched_lock);
                cdr_sched = ast_sched_add(sched, batchtime * 1000, submit_scheduled_batch, NULL);
+               ast_mutex_unlock(&cdr_sched_lock);
                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");
@@ -1599,11 +1647,12 @@ static int do_reload(int reload)
                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_mutex_lock(&cdr_sched_lock);
                        AST_SCHED_DEL(sched, cdr_sched);
+                       ast_mutex_unlock(&cdr_sched_lock);
                } else {
                        ast_cli_register(&cli_submit);
                        ast_register_atexit(ast_cdr_engine_term);
-                       res = 0;
                }
        /* if this reload disabled the CDR and/or batch mode and there is a background thread,
           kill it */
@@ -1616,27 +1665,38 @@ static int do_reload(int reload)
                ast_cond_destroy(&cdr_pending_cond);
                ast_cli_unregister(&cli_submit);
                ast_unregister_atexit(ast_cdr_engine_term);
-               res = 0;
                /* 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();
                }
-       } else {
-               res = 0;
        }
 
        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");
+}
 
-       return res;
+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);
+
+       ast_cli_unregister(&cli_status);
+       ast_sched_context_destroy(sched);
+       sched = NULL;
+       ast_free(batch);
+       batch = NULL;
 }
 
 int ast_cdr_engine_init(void)
 {
-       int res;
-
        sched = ast_sched_context_create();
        if (!sched) {
                ast_log(LOG_ERROR, "Unable to create schedule context.\n");
@@ -1644,15 +1704,10 @@ int ast_cdr_engine_init(void)
        }
 
        ast_cli_register(&cli_status);
+       do_reload(0);
+       ast_register_atexit(cdr_engine_shutdown);
 
-       res = do_reload(0);
-       if (res) {
-               ast_mutex_lock(&cdr_batch_lock);
-               res = init_batch();
-               ast_mutex_unlock(&cdr_batch_lock);
-       }
-
-       return res;
+       return 0;
 }
 
 /* \note This actually gets called a couple of times at shutdown.  Once, before we start
@@ -1664,7 +1719,8 @@ void ast_cdr_engine_term(void)
 
 int ast_cdr_engine_reload(void)
 {
-       return do_reload(1);
+       do_reload(1);
+       return 0;
 }
 
 int ast_cdr_data_add_structure(struct ast_data *tree, struct ast_cdr *cdr, int recur)