<p>N A has uploaded this change for <strong>review</strong>.</p><p><a href="https://gerrit.asterisk.org/c/asterisk/+/19431">View Change</a></p><pre style="font-family: monospace,monospace; white-space: pre-wrap;">cdr: Allow bridging and dial state changes to be ignored.<br><br>Allows bridging, parking, and dial messages to be globally<br>ignored for all CDRs such that only a single CDR record<br>is generated per channel.<br><br>This is useful when CDRs should endure for the lifetime of<br>an entire channel and bridging and dial updates in the<br>dialplan should not result in multiple CDR records being<br>created for the call. With the ignore bridging option,<br>bridging changes have no impact on the channel's CDRs.<br>With the ignore dial state option, multiple Dials and their<br>outcomes have no impact on the channel's CDRs. The<br>last disposition on the channel is preserved in the CDR,<br>so the actual disposition of the call remains available.<br><br>These two options can reduce the amount of "CDR hacks" that<br>have hitherto been necessary to ensure that CDR was not<br>"spoiled" by these messages if that was undesired, such as<br>putting a dummy optimization-disabled local channel between<br>the caller and the actual call and putting the CDR on the channel<br>in the middle to ensure that CDR would persist for the entire<br>call and properly record start, answer, and end times.<br>Enabling these options is desirable when calls correspond<br>to the entire lifetime of channels and the CDR should<br>reflect that.<br><br>Current default behavior remains unchanged.<br><br>ASTERISK-30091 #close<br><br>Change-Id: I393981af42732ec5ac3ff9266444abb453b7c832<br>---<br>M configs/samples/cdr.conf.sample<br>A doc/CHANGES-staging/cdr_ignore.txt<br>M include/asterisk/cdr.h<br>M main/cdr.c<br>4 files changed, 134 insertions(+), 9 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/31/19431/1</pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/configs/samples/cdr.conf.sample b/configs/samples/cdr.conf.sample</span><br><span>index 6331760..46ddb96 100644</span><br><span>--- a/configs/samples/cdr.conf.sample</span><br><span>+++ b/configs/samples/cdr.conf.sample</span><br><span>@@ -32,6 +32,17 @@</span><br><span> ; is "no".</span><br><span> ;congestion = no</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+; Define whether or not to ignore bridging changes in CDRs. This prevents</span><br><span style="color: hsl(120, 100%, 40%);">+; bridging changes from resulting in multiple CDRs for different parts of</span><br><span style="color: hsl(120, 100%, 40%);">+; a call. Default is "no". This setting cannot be changed on a reload.</span><br><span style="color: hsl(120, 100%, 40%);">+;ignorestatechanges = no</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+; Define whether or not to ignore dial updates in CDRs. This prevents</span><br><span style="color: hsl(120, 100%, 40%);">+; dial updates from resulting in multiple CDRs for different parts of</span><br><span style="color: hsl(120, 100%, 40%);">+; a call. The last disposition on the channel will be used for the CDR.</span><br><span style="color: hsl(120, 100%, 40%);">+; Use with caution. Default is "no".</span><br><span style="color: hsl(120, 100%, 40%);">+;ignoredialchanges = no</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> ; Normally, CDR's are not closed out until after all extensions are finished</span><br><span> ; executing.  By enabling this option, the CDR will be ended before executing</span><br><span> ; the "h" extension and hangup handlers so that CDR values such as "end" and</span><br><span>diff --git a/doc/CHANGES-staging/cdr_ignore.txt b/doc/CHANGES-staging/cdr_ignore.txt</span><br><span>new file mode 100644</span><br><span>index 0000000..e82f404</span><br><span>--- /dev/null</span><br><span>+++ b/doc/CHANGES-staging/cdr_ignore.txt</span><br><span>@@ -0,0 +1,6 @@</span><br><span style="color: hsl(120, 100%, 40%);">+Subject: cdr</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+Two new options have been added which allow</span><br><span style="color: hsl(120, 100%, 40%);">+bridging and dial state changes to be ignored</span><br><span style="color: hsl(120, 100%, 40%);">+in CDRs, which can be useful if a single CDR</span><br><span style="color: hsl(120, 100%, 40%);">+is desired for a channel.</span><br><span>diff --git a/include/asterisk/cdr.h b/include/asterisk/cdr.h</span><br><span>index 2bbfbdb..06307c9 100644</span><br><span>--- a/include/asterisk/cdr.h</span><br><span>+++ b/include/asterisk/cdr.h</span><br><span>@@ -225,6 +225,8 @@</span><br><span>         CDR_INITIATED_SECONDS = 1 << 5,     /*!< Include microseconds into the billing time */</span><br><span>      CDR_DEBUG = 1 << 6,                 /*!< Enables extra debug statements */</span><br><span>  CDR_CHANNEL_DEFAULT_ENABLED = 1 << 7, /*!< Whether CDR is enabled for each channel by default */</span><br><span style="color: hsl(120, 100%, 40%);">+     CDR_IGNORE_STATE_CHANGES = 1 << 8,        /*!< Whether to ignore bridge and other call state change events */</span><br><span style="color: hsl(120, 100%, 40%);">+        CDR_IGNORE_DIAL_CHANGES = 1 << 9, /*!< Whether to ignore dial state changes */</span><br><span> };</span><br><span> </span><br><span> /*! \brief CDR Batch Mode settings */</span><br><span>diff --git a/main/cdr.c b/main/cdr.c</span><br><span>index b059082..8f1f30b 100644</span><br><span>--- a/main/cdr.c</span><br><span>+++ b/main/cdr.c</span><br><span>@@ -111,6 +111,29 @@</span><br><span>                                       to undisable (enable) CDR for a call.</para></span><br><span>                                   </description></span><br><span>                                 </configOption></span><br><span style="color: hsl(120, 100%, 40%);">+                         <configOption name="ignorestatechanges" default="no"></span><br><span style="color: hsl(120, 100%, 40%);">+                                       <synopsis>Whether CDR is updated or forked by bridging changes.</synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+                                        <description><para>Define whether or not CDR should be updated by bridging changes.</span><br><span style="color: hsl(120, 100%, 40%);">+                                       This includes entering and leaving bridges and call parking.</para></span><br><span style="color: hsl(120, 100%, 40%);">+                                     <para>If this is set to "no", bridging changes will be ignored for all CDRs.</span><br><span style="color: hsl(120, 100%, 40%);">+                                  This should only be done if these events should not affect CDRs and are undesired,</span><br><span style="color: hsl(120, 100%, 40%);">+                                    such as to use a single CDR for the lifetime of the channel.</para></span><br><span style="color: hsl(120, 100%, 40%);">+                                     <para>This setting cannot be changed on a reload.</para></span><br><span style="color: hsl(120, 100%, 40%);">+                                  </description></span><br><span style="color: hsl(120, 100%, 40%);">+                          </configOption></span><br><span style="color: hsl(120, 100%, 40%);">+                         <configOption name="ignoredialchanges" default="no"></span><br><span style="color: hsl(120, 100%, 40%);">+                                        <synopsis>Whether CDR is updated or forked by dial updates.</synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+                                    <description><para>Define whether or not CDR should be updated by dial updates.</para></span><br><span style="color: hsl(120, 100%, 40%);">+                                      <para>If this is set to "no", a single CDR will be used for the channel, even if</span><br><span style="color: hsl(120, 100%, 40%);">+                                      multiple endpoints or destinations are dialed sequentially. Note that you will also</span><br><span style="color: hsl(120, 100%, 40%);">+                                   lose detailed nonanswer dial dispositions if this option is enabled, which may not be acceptable,</span><br><span style="color: hsl(120, 100%, 40%);">+                                     e.g. instead of detailed no-answer dispositions like BUSY and CONGESTION, the disposition</span><br><span style="color: hsl(120, 100%, 40%);">+                                     will always be NO ANSWER if the channel was unanswered (it will still be ANSWERED</span><br><span style="color: hsl(120, 100%, 40%);">+                                     if the channel was answered).</para></span><br><span style="color: hsl(120, 100%, 40%);">+                                    <para>This option should be enabled if a single CDR is desired for the lifetime of</span><br><span style="color: hsl(120, 100%, 40%);">+                                      the channel.</para></span><br><span style="color: hsl(120, 100%, 40%);">+                                     </description></span><br><span style="color: hsl(120, 100%, 40%);">+                          </configOption></span><br><span>                                <configOption name="unanswered"></span><br><span>                                     <synopsis>Log calls that are never answered and don't set an outgoing party.</synopsis></span><br><span>                                      <description><para></span><br><span>@@ -208,6 +231,8 @@</span><br><span> #define DEFAULT_END_BEFORE_H_EXTEN "1"</span><br><span> #define DEFAULT_INITIATED_SECONDS "0"</span><br><span> #define DEFAULT_CHANNEL_ENABLED "1"</span><br><span style="color: hsl(120, 100%, 40%);">+#define DEFAULT_IGNORE_STATE_CHANGES "0"</span><br><span style="color: hsl(120, 100%, 40%);">+#define DEFAULT_IGNORE_DIAL_CHANGES "0"</span><br><span> </span><br><span> #define DEFAULT_BATCH_SIZE "100"</span><br><span> #define MAX_BATCH_SIZE 1000</span><br><span>@@ -222,6 +247,7 @@</span><br><span>  } while (0)</span><br><span> </span><br><span> static int cdr_debug_enabled;</span><br><span style="color: hsl(120, 100%, 40%);">+static int dial_changes_ignored;</span><br><span> </span><br><span> #define CDR_DEBUG(fmt, ...) \</span><br><span>      do { \</span><br><span>@@ -2170,6 +2196,10 @@</span><br><span>                      if (!it_cdr->fn_table->process_dial_begin) {</span><br><span>                           continue;</span><br><span>                    }</span><br><span style="color: hsl(120, 100%, 40%);">+                     if (dial_changes_ignored) {</span><br><span style="color: hsl(120, 100%, 40%);">+                           CDR_DEBUG("%p - Ignoring Dial Begin message\n", it_cdr);</span><br><span style="color: hsl(120, 100%, 40%);">+                            continue;</span><br><span style="color: hsl(120, 100%, 40%);">+                     }</span><br><span>                    CDR_DEBUG("%p - Processing Dial Begin message for channel %s, peer %s\n",</span><br><span>                          it_cdr,</span><br><span>                              caller ? caller->base->name : "(none)",</span><br><span>@@ -2181,6 +2211,12 @@</span><br><span>                     if (!it_cdr->fn_table->process_dial_end) {</span><br><span>                             continue;</span><br><span>                    }</span><br><span style="color: hsl(120, 100%, 40%);">+                     if (dial_changes_ignored) {</span><br><span style="color: hsl(120, 100%, 40%);">+                           /* Set the disposition, and do nothing else. */</span><br><span style="color: hsl(120, 100%, 40%);">+                               it_cdr->disposition = dial_status_to_disposition(dial_status);</span><br><span style="color: hsl(120, 100%, 40%);">+                             CDR_DEBUG("%p - Setting disposition and that's it (%s)\n", it_cdr, dial_status);</span><br><span style="color: hsl(120, 100%, 40%);">+                                continue;</span><br><span style="color: hsl(120, 100%, 40%);">+                     }</span><br><span>                    CDR_DEBUG("%p - Processing Dial End message for channel %s, peer %s\n",</span><br><span>                            it_cdr,</span><br><span>                              caller ? caller->base->name : "(none)",</span><br><span>@@ -2192,15 +2228,19 @@</span><br><span>            }</span><br><span>    }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   /* If no CDR handled a dial begin message, make a new one */</span><br><span style="color: hsl(0, 100%, 40%);">-    if (res && ast_strlen_zero(dial_status)) {</span><br><span style="color: hsl(0, 100%, 40%);">-              struct cdr_object *new_cdr;</span><br><span style="color: hsl(120, 100%, 40%);">+   /* If we're ignoring dial changes, don't allow multiple CDRs for this channel. */</span><br><span style="color: hsl(120, 100%, 40%);">+     if (!dial_changes_ignored) {</span><br><span style="color: hsl(120, 100%, 40%);">+          /* If no CDR handled a dial begin message, make a new one */</span><br><span style="color: hsl(120, 100%, 40%);">+          if (res && ast_strlen_zero(dial_status)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                    struct cdr_object *new_cdr;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-         new_cdr = cdr_object_create_and_append(cdr, stasis_message_timestamp(message));</span><br><span style="color: hsl(0, 100%, 40%);">-         if (new_cdr) {</span><br><span style="color: hsl(0, 100%, 40%);">-                  new_cdr->fn_table->process_dial_begin(new_cdr, caller, peer);</span><br><span style="color: hsl(120, 100%, 40%);">+                   new_cdr = cdr_object_create_and_append(cdr, stasis_message_timestamp(message));</span><br><span style="color: hsl(120, 100%, 40%);">+                       if (new_cdr) {</span><br><span style="color: hsl(120, 100%, 40%);">+                                new_cdr->fn_table->process_dial_begin(new_cdr, caller, peer);</span><br><span style="color: hsl(120, 100%, 40%);">+                   }</span><br><span>            }</span><br><span>    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>  ao2_unlock(cdr);</span><br><span>     ao2_cleanup(cdr);</span><br><span> }</span><br><span>@@ -4200,6 +4240,8 @@</span><br><span>               ast_cli(a->fd, "  Log calls by default:       %s\n", ast_test_flag(&mod_cfg->general->settings, CDR_CHANNEL_DEFAULT_ENABLED) ? "Yes" : "No");</span><br><span>             ast_cli(a->fd, "  Log unanswered calls:       %s\n", ast_test_flag(&mod_cfg->general->settings, CDR_UNANSWERED) ? "Yes" : "No");</span><br><span>          ast_cli(a->fd, "  Log congestion:             %s\n\n", ast_test_flag(&mod_cfg->general->settings, CDR_CONGESTION) ? "Yes" : "No");</span><br><span style="color: hsl(120, 100%, 40%);">+         ast_cli(a->fd, "  Ignore bridging changes:    %s\n\n", ast_test_flag(&mod_cfg->general->settings, CDR_IGNORE_STATE_CHANGES) ? "Yes" : "No");</span><br><span style="color: hsl(120, 100%, 40%);">+               ast_cli(a->fd, "  Ignore dial state changes:  %s\n\n", ast_test_flag(&mod_cfg->general->settings, CDR_IGNORE_DIAL_CHANGES) ? "Yes" : "No");</span><br><span>               if (ast_test_flag(&mod_cfg->general->settings, CDR_BATCHMODE)) {</span><br><span>                   ast_cli(a->fd, "* Batch Mode Settings\n");</span><br><span>                      ast_cli(a->fd, "  -------------------\n");</span><br><span>@@ -4379,6 +4421,8 @@</span><br><span>              aco_option_register(&cfg_info, "size", ACO_EXACT, general_options, DEFAULT_BATCH_SIZE, OPT_UINT_T, PARSE_IN_RANGE, FLDSET(struct ast_cdr_config, batch_settings.size), 0, MAX_BATCH_SIZE);</span><br><span>             aco_option_register(&cfg_info, "time", ACO_EXACT, general_options, DEFAULT_BATCH_TIME, OPT_UINT_T, PARSE_IN_RANGE, FLDSET(struct ast_cdr_config, batch_settings.time), 1, MAX_BATCH_TIME);</span><br><span>             aco_option_register(&cfg_info, "channeldefaultenabled", ACO_EXACT, general_options, DEFAULT_CHANNEL_ENABLED, OPT_BOOLFLAG_T, 1, FLDSET(struct ast_cdr_config, settings), CDR_CHANNEL_DEFAULT_ENABLED);</span><br><span style="color: hsl(120, 100%, 40%);">+          aco_option_register(&cfg_info, "ignorestatechanges", ACO_EXACT, general_options, DEFAULT_IGNORE_STATE_CHANGES, OPT_BOOLFLAG_T, 1, FLDSET(struct ast_cdr_config, settings), CDR_IGNORE_STATE_CHANGES);</span><br><span style="color: hsl(120, 100%, 40%);">+           aco_option_register(&cfg_info, "ignoredialchanges", ACO_EXACT, general_options, DEFAULT_IGNORE_DIAL_CHANGES, OPT_BOOLFLAG_T, 1, FLDSET(struct ast_cdr_config, settings), CDR_IGNORE_DIAL_CHANGES);</span><br><span>     }</span><br><span> </span><br><span>        if (aco_process_config(&cfg_info, reload) == ACO_PROCESS_ERROR) {</span><br><span>@@ -4541,6 +4585,7 @@</span><br><span> </span><br><span> static int load_module(void)</span><br><span> {</span><br><span style="color: hsl(120, 100%, 40%);">+        struct module_config *mod_cfg = NULL;</span><br><span>        if (process_config(0)) {</span><br><span>             return AST_MODULE_LOAD_FAILURE;</span><br><span>      }</span><br><span>@@ -4561,13 +4606,36 @@</span><br><span>          return AST_MODULE_LOAD_FAILURE;</span><br><span>      }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ mod_cfg = ao2_global_obj_ref(module_configs);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>      stasis_message_router_add(stasis_router, ast_channel_snapshot_type(), handle_channel_snapshot_update_message, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        /* Always process dial messages, because even if we ignore most of it, we do want the dial status for the disposition. */</span><br><span>    stasis_message_router_add(stasis_router, ast_channel_dial_type(), handle_dial_message, NULL);</span><br><span style="color: hsl(0, 100%, 40%);">-   stasis_message_router_add(stasis_router, ast_channel_entered_bridge_type(), handle_bridge_enter_message, NULL);</span><br><span style="color: hsl(0, 100%, 40%);">- stasis_message_router_add(stasis_router, ast_channel_left_bridge_type(), handle_bridge_leave_message, NULL);</span><br><span style="color: hsl(0, 100%, 40%);">-    stasis_message_router_add(stasis_router, ast_parked_call_type(), handle_parked_call_message, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!mod_cfg || !ast_test_flag(&mod_cfg->general->settings, CDR_IGNORE_DIAL_CHANGES)) {</span><br><span style="color: hsl(120, 100%, 40%);">+             dial_changes_ignored = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+     } else {</span><br><span style="color: hsl(120, 100%, 40%);">+              dial_changes_ignored = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+             CDR_DEBUG("Dial messages will be mostly ignored\n");</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* If explicitly instructed to ignore call state changes, then ignore bridging events, parking, etc. */</span><br><span style="color: hsl(120, 100%, 40%);">+       if (!mod_cfg || !ast_test_flag(&mod_cfg->general->settings, CDR_IGNORE_STATE_CHANGES)) {</span><br><span style="color: hsl(120, 100%, 40%);">+            stasis_message_router_add(stasis_router, ast_channel_entered_bridge_type(), handle_bridge_enter_message, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+               stasis_message_router_add(stasis_router, ast_channel_left_bridge_type(), handle_bridge_leave_message, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+          stasis_message_router_add(stasis_router, ast_parked_call_type(), handle_parked_call_message, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+   } else {</span><br><span style="color: hsl(120, 100%, 40%);">+              CDR_DEBUG("All bridge and parking messages will be ignored\n");</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>  stasis_message_router_add(stasis_router, cdr_sync_message_type(), handle_cdr_sync_message, NULL);</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ if (mod_cfg) {</span><br><span style="color: hsl(120, 100%, 40%);">+                ao2_cleanup(mod_cfg);</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+              ast_log(LOG_WARNING, "Unable to obtain CDR configuration during module load?\n");</span><br><span style="color: hsl(120, 100%, 40%);">+   }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>  active_cdrs_master = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX, 0,</span><br><span>           AST_NUM_CHANNEL_BUCKETS, cdr_master_hash_fn, NULL, cdr_master_cmp_fn);</span><br><span>       if (!active_cdrs_master) {</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.asterisk.org/c/asterisk/+/19431">change 19431</a>. To unsubscribe, or for help writing mail filters, visit <a href="https://gerrit.asterisk.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.asterisk.org/c/asterisk/+/19431"/><meta itemprop="name" content="View Change"/></div></div>

<div style="display:none"> Gerrit-Project: asterisk </div>
<div style="display:none"> Gerrit-Branch: 19 </div>
<div style="display:none"> Gerrit-Change-Id: I393981af42732ec5ac3ff9266444abb453b7c832 </div>
<div style="display:none"> Gerrit-Change-Number: 19431 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: N A <mail@interlinked.x10host.com> </div>
<div style="display:none"> Gerrit-MessageType: newchange </div>