]> Kevux Git Server - fll/commitdiff
Progress: controller program.
authorKevin Day <thekevinday@gmail.com>
Wed, 16 Dec 2020 04:15:19 +0000 (22:15 -0600)
committerKevin Day <thekevinday@gmail.com>
Wed, 16 Dec 2020 04:15:19 +0000 (22:15 -0600)
level_3/controller/c/private-common.h
level_3/controller/c/private-controller.c
level_3/controller/c/private-entry.c
level_3/controller/c/private-entry.h
level_3/controller/c/private-rule.c
level_3/controller/c/private-rule.h

index 881bcd332b008ef1d1737a94d714ac51812a972e..c6a6e7e076c9d35976d84ed93a901f0f0fe60d2c 100644 (file)
@@ -27,12 +27,14 @@ extern "C" {
   #define controller_string_environment   "environment"
   #define controller_string_failsafe      "failsafe"
   #define controller_string_group         "group"
+  #define controller_string_how           "how"
   #define controller_string_item          "item"
   #define controller_string_kill          "kill"
   #define controller_string_main          "main"
   #define controller_string_method        "method"
   #define controller_string_name          "name"
   #define controller_string_need          "need"
+  #define controller_string_no            "no"
   #define controller_string_parameter     "parameter"
   #define controller_string_path          "path"
   #define controller_string_pid           "pid"
@@ -47,6 +49,7 @@ extern "C" {
   #define controller_string_setting       "setting"
   #define controller_string_start         "start"
   #define controller_string_stop          "stop"
+  #define controller_string_synchronous   "synchronous"
   #define controller_string_timeout       "timeout"
   #define controller_string_type          "type"
   #define controller_string_use           "use"
@@ -54,6 +57,7 @@ extern "C" {
   #define controller_string_wait          "wait"
   #define controller_string_want          "want"
   #define controller_string_wish          "wish"
+  #define controller_string_yes           "yes"
 
   #define controller_string_action_length        6
   #define controller_string_actions_length       7
@@ -69,12 +73,14 @@ extern "C" {
   #define controller_string_environment_length   11
   #define controller_string_failsafe_length      8
   #define controller_string_group_length         5
+  #define controller_string_how_length           3
   #define controller_string_item_length          4
   #define controller_string_kill_length          4
   #define controller_string_main_length          4
   #define controller_string_method_length        6
   #define controller_string_name_length          4
   #define controller_string_need_length          4
+  #define controller_string_no_length            2
   #define controller_string_parameter_length     9
   #define controller_string_path_length          4
   #define controller_string_pid_length           3
@@ -89,6 +95,7 @@ extern "C" {
   #define controller_string_setting_length       7
   #define controller_string_start_length         5
   #define controller_string_stop_length          4
+  #define controller_string_synchronous_length   11
   #define controller_string_timeout_length       7
   #define controller_string_type_length          4
   #define controller_string_use_length           3
@@ -96,6 +103,7 @@ extern "C" {
   #define controller_string_wait_length          4
   #define controller_string_want_length          4
   #define controller_string_wish_length          4
+  #define controller_string_yes_length           3
 #endif // _di_controller_string_
 
 #ifndef _di_controller_rule_action_t_
@@ -235,6 +243,10 @@ extern "C" {
     controller_rule_setting_type_wish,
   };
 
+  #define controller_rule_option_simulate     0x1
+  #define controller_rule_option_asynchronous 0x2
+  #define controller_rule_option_wait         0x4
+
   typedef struct {
     f_status_t status;
     f_number_signed_t process; // @todo: for background/threaded support (ideally should hold the process id, but remove if this ends up not being the strategy) (this can also be used by the parent/main process to check to see if the child no longer a child of this process).
index 27cd1e4d029c7309a6c2017f8cf8482faf4a922d..92221c7e5da57b99ca1af2ca45ab52ab82fa1be9 100644 (file)
@@ -228,15 +228,20 @@ extern "C" {
 
       // report pid file error but because this could be an "init" program, consider the pid file as optional and continue on.
       if (F_status_is_error(status)) {
-        fll_error_file_print(data.error, F_status_set_fine(status), "controller_file_pid_create", F_true, setting->path_pid.string, "create", fll_error_file_type_file);
-
-        controller_entry_error_print(data.error, *cache);
 
         // always return immediately on memory errors.
         if (F_status_set_fine(status) == F_memory_not || F_status_set_fine(status) == F_memory_allocation || F_status_set_fine(status) == F_memory_reallocation) {
+          fll_error_file_print(data.error, F_status_set_fine(status), "controller_file_pid_create", F_true, setting->path_pid.string, "create", fll_error_file_type_file);
+
+          controller_entry_error_print(data.error, *cache);
+
           return status;
         }
 
+        fll_error_file_print(data.warning, F_status_set_fine(status), "controller_file_pid_create", F_true, setting->path_pid.string, "create", fll_error_file_type_file);
+
+        controller_entry_error_print(data.warning, *cache);
+
         status = F_none;
       }
     }
@@ -472,6 +477,8 @@ extern "C" {
     f_array_length_t at_i = 0;
     f_array_length_t at_j = 1;
 
+    uint8_t rule_options = 0;
+
     controller_entry_actions_t *actions = 0;
 
     const bool simulate = data.parameters[controller_parameter_test].result == f_console_result_found;
@@ -540,14 +547,96 @@ extern "C" {
         }
 
         if (F_status_is_error(actions->array[cache->ats.array[at_j]].status)) {
-          if (simulate) {
-            fprintf(data.output.stream, "%c", f_string_eol_s[0]);
-            fprintf(data.output.stream, "The entry item action '");
-            fprintf(data.output.stream, "%s%s%s", data.context.set.title.before->string, cache->name_action.string, data.context.set.title.after->string);
-            fprintf(data.output.stream, "' is in a %sfailed%s state, skipping execution.%c", data.error.context.before->string, data.error.context.after->string, f_string_eol_s[0]);
+
+          if (actions->array[cache->ats.array[at_j]].type == controller_entry_action_type_rule) {
+
+            if (simulate) {
+              fprintf(data.output.stream, "%c", f_string_eol_s[0]);
+              fprintf(data.output.stream, "The entry item action '");
+              fprintf(data.output.stream, "%s%s%s", data.context.set.title.before->string, cache->name_action.string, data.context.set.title.after->string);
+
+              if (actions->array[cache->ats.array[at_j]].parameters.used) {
+                fprintf(data.output.stream, " ");
+                fprintf(data.output.stream, "%s", data.context.set.notable.before->string);
+                controller_entry_action_parameters_print(data.output.stream, actions->array[cache->ats.array[at_j]]);
+                fprintf(data.output.stream, "%s", data.context.set.notable.after->string);
+              }
+
+              fprintf(data.output.stream, "' is %s and is in a %sfailed%s state, skipping execution.%c", actions->array[cache->ats.array[at_j]].code & controller_entry_rule_code_require ? "required" : "optional", data.error.context.before->string, data.error.context.after->string, f_string_eol_s[0]);
+            }
+            else if (actions->array[cache->ats.array[at_j]].code & controller_entry_rule_code_require) {
+
+              if (data.error.verbosity != f_console_verbosity_quiet) {
+                fprintf(data.error.to.stream, "%c", f_string_eol_s[0]);
+                fprintf(data.error.to.stream, "%s%sThe entry item action '", data.error.context.before->string, data.error.prefix ? data.error.prefix : f_string_empty_s);
+                fprintf(data.error.to.stream, "%s%s%s", data.error.context.after->string, data.error.notable.before->string, cache->name_action.string);
+
+                if (actions->array[cache->ats.array[at_j]].parameters.used) {
+                  fprintf(data.error.to.stream, " ");
+                  controller_entry_action_parameters_print(data.error.to.stream, actions->array[cache->ats.array[at_j]]);
+                }
+
+                fprintf(data.error.to.stream, "%s%s' is ", data.error.notable.after->string, data.error.context.before->string);
+                fprintf(data.error.to.stream, "%s%srequired%s", data.error.context.after->string, data.error.notable.before->string, data.error.notable.after->string);
+                fprintf(data.error.to.stream, "%s and is in a ", data.error.context.before->string);
+                fprintf(data.error.to.stream, "%s%sfailed%s", data.error.context.after->string, data.error.notable.before->string, data.error.notable.after->string);
+                fprintf(data.error.to.stream, "%s state, skipping execution.%s%c", data.error.context.before->string, data.error.context.after->string, f_string_eol_s[0]);
+              }
+
+              controller_entry_error_print(data.error, *cache);
+
+              return F_status_is_error(F_require);
+            }
+            else if (data.warning.verbosity == f_console_verbosity_debug) {
+              fprintf(data.warning.to.stream, "%c", f_string_eol_s[0]);
+              fprintf(data.warning.to.stream, "%s%sThe entry item action '", data.warning.context.before->string, data.warning.prefix ? data.warning.prefix : f_string_empty_s);
+              fprintf(data.warning.to.stream, "%s%s%s", data.warning.context.after->string, data.warning.notable.before->string, cache->name_action.string);
+
+              if (actions->array[cache->ats.array[at_j]].parameters.used) {
+                fprintf(data.warning.to.stream, " ");
+                controller_entry_action_parameters_print(data.warning.to.stream, actions->array[cache->ats.array[at_j]]);
+              }
+
+              fprintf(data.warning.to.stream, "%s%s' is ", data.warning.notable.after->string, data.warning.context.before->string);
+              fprintf(data.warning.to.stream, "%s%srequired%s", data.warning.context.after->string, data.warning.notable.before->string, data.warning.notable.after->string);
+              fprintf(data.warning.to.stream, "%s and is in a ", data.warning.context.before->string);
+              fprintf(data.warning.to.stream, "%s%sfailed%s", data.warning.context.after->string, data.warning.notable.before->string, data.warning.notable.after->string);
+              fprintf(data.warning.to.stream, "%s state, skipping execution.%s%c", data.warning.context.before->string, data.warning.context.after->string, f_string_eol_s[0]);
+
+              controller_entry_error_print(data.warning, *cache);
+            }
           }
           else {
-            // @todo check to see if this rule is "required" and if so immediately fail, otherwise report a failure as a warning (normal verbosity, not debug verbosity).
+            if (simulate) {
+              fprintf(data.output.stream, "%c", f_string_eol_s[0]);
+              fprintf(data.output.stream, "The entry item action '");
+              fprintf(data.output.stream, "%s%s%s", data.context.set.title.before->string, cache->name_action.string, data.context.set.title.after->string);
+
+              if (actions->array[cache->ats.array[at_j]].parameters.used) {
+                fprintf(data.output.stream, " ");
+                fprintf(data.output.stream, "%s", data.context.set.notable.before->string);
+                controller_entry_action_parameters_print(data.output.stream, actions->array[cache->ats.array[at_j]]);
+                fprintf(data.output.stream, "%s", data.context.set.notable.after->string);
+              }
+
+              fprintf(data.output.stream, "' is in a %sfailed%s state, skipping.%c", data.error.context.before->string, data.error.context.after->string, f_string_eol_s[0]);
+            }
+            else if (data.warning.verbosity == f_console_verbosity_debug) {
+              fprintf(data.warning.to.stream, "%c", f_string_eol_s[0]);
+              fprintf(data.warning.to.stream, "%s%sThe entry item action '", data.warning.context.before->string, data.warning.prefix ? data.warning.prefix : f_string_empty_s);
+              fprintf(data.warning.to.stream, "%s%s", data.warning.notable.before->string, cache->name_action.string);
+
+              if (actions->array[cache->ats.array[at_j]].parameters.used) {
+                fprintf(data.warning.to.stream, " ");
+                controller_entry_action_parameters_print(data.warning.to.stream, actions->array[cache->ats.array[at_j]]);
+              }
+
+              fprintf(data.warning.to.stream, "%s' is in a ", data.warning.notable.after->string);
+              fprintf(data.warning.to.stream, "%s%sfailed%s", data.warning.context.after->string, data.warning.notable.before->string, data.warning.notable.after->string);
+              fprintf(data.warning.to.stream, "%s state, skipping.%s%c", data.warning.context.before->string, data.warning.context.after->string, f_string_eol_s[0]);
+
+              controller_entry_error_print(data.warning, *cache);
+            }
           }
 
           continue;
@@ -574,7 +663,7 @@ extern "C" {
             fprintf(data.output.stream, "%c", f_string_eol_s[0]);
             fprintf(data.output.stream, "Ignoring entry item action '");
             fprintf(data.output.stream, "%s%s%s", data.context.set.title.before->string, controller_string_ready, data.context.set.title.after->string);
-            fprintf(data.output.stream, "', state is already ready.%c", f_string_eol_s[0]);
+            fprintf(data.output.stream, "', state already is ready.%c", f_string_eol_s[0]);
           }
         }
         else if (actions->array[cache->ats.array[at_j]].type == controller_entry_action_type_item) {
@@ -667,9 +756,43 @@ extern "C" {
             fprintf(data.output.stream, "'.%c", f_string_eol_s[0]);
           }
 
+          // the rule is not yet loaded, ensure that it is loaded.
           if (at == setting->rules.used) {
+
+            // rule execution will re-use the existing cache, so save the current cache.
+            const f_array_length_t cache_line_action = cache->line_action;
+            const f_array_length_t cache_line_item = cache->line_item;
+
+            const f_string_length_t cache_name_action_used = cache->name_action.used;
+            const f_string_length_t cache_name_item_used = cache->name_item.used;
+            const f_string_length_t cache_name_file_used = cache->name_file.used;
+
+            char cache_name_action[cache_name_action_used];
+            char cache_name_item[cache_name_item_used];
+            char cache_name_file[cache_name_file_used];
+
+            memcpy(cache_name_action, cache->name_action.string, cache->name_action.used);
+            memcpy(cache_name_item, cache->name_item.string, cache->name_item.used);
+            memcpy(cache_name_file, cache->name_file.string, cache->name_file.used);
+
             status = controller_rule_read(data, *setting, rule_id, cache, &setting->rules.array[setting->rules.used]);
 
+            // restore cache.
+            memcpy(cache->name_action.string, cache_name_action, cache_name_action_used);
+            memcpy(cache->name_item.string, cache_name_item, cache_name_item_used);
+            memcpy(cache->name_file.string, cache_name_file, cache_name_file_used);
+
+            cache->name_action.string[cache_name_action_used] = 0;
+            cache->name_item.string[cache_name_item_used] = 0;
+            cache->name_file.string[cache_name_file_used] = 0;
+
+            cache->name_action.used = cache_name_action_used;
+            cache->name_item.used = cache_name_item_used;
+            cache->name_file.used = cache_name_file_used;
+
+            cache->line_action = cache_line_action;
+            cache->line_item = cache_line_item;
+
             if (F_status_is_error(status)) {
               controller_entry_error_print(data.error, *cache);
 
@@ -699,8 +822,22 @@ extern "C" {
             memcpy(cache_name_file, cache->name_file.string, cache->name_file.used);
 
             if (actions->array[cache->ats.array[at_j]].type == controller_entry_action_type_rule) {
+              rule_options = 0;
+
+              if (simulate) {
+                rule_options |= controller_rule_option_simulate;
+              }
+
+              if (actions->array[cache->ats.array[at_j]].code & controller_entry_rule_code_asynchronous) {
+                rule_options |= controller_rule_option_asynchronous;
+              }
+
+              if (actions->array[cache->ats.array[at_j]].code & controller_entry_rule_code_wait) {
+                rule_options |= controller_rule_option_wait;
+              }
+
               // @todo: this will also need to support the asynchronous/wait behavior.
-              status = controller_rule_process(data, at, simulate, setting, cache);
+              status = controller_rule_process(data, at, rule_options, setting, cache);
             }
 
             // restore cache.
@@ -793,7 +930,6 @@ extern "C" {
             }
           }
         }
-
       } // for
 
       cache->line_action = 0;
index 6b677b6506fc070d9d8f9551684fb70c11563da8..ba80d290393eb4b6577c36c8ffb6f33365d9d441 100644 (file)
@@ -7,6 +7,24 @@
 extern "C" {
 #endif
 
+#ifndef _di_controller_entry_action_parameters_print_
+  void controller_entry_action_parameters_print(FILE * const stream, const controller_entry_action_t action) {
+
+    f_array_length_t index = 0;
+
+    for (;;) {
+
+      f_print_dynamic(stream, action.parameters.array[index]);
+
+      ++index;
+
+      if (index == action.parameters.used) break;
+
+      fprintf(stream, " ");
+    } // for
+  }
+#endif // _di_controller_entry_action_parameters_print_
+
 #ifndef _di_controller_entry_action_type_name_
   f_string_static_t controller_entry_action_type_name(const uint8_t type) {
 
@@ -396,13 +414,13 @@ extern "C" {
             for (j = 2; j < action->parameters.used; ++j) {
 
               if (fl_string_dynamic_compare_string(controller_string_asynchronous, action->parameters.array[j], controller_string_asynchronous_length) == F_equal_to) {
-                action->code = controller_entry_rule_code_asynchronous;
+                action->code |= controller_entry_rule_code_asynchronous;
               }
               else if (fl_string_dynamic_compare_string(controller_string_require, action->parameters.array[j], controller_string_require_length) == F_equal_to) {
-                action->code = controller_entry_rule_code_require;
+                action->code |= controller_entry_rule_code_require;
               }
               else if (fl_string_dynamic_compare_string(controller_string_wait, action->parameters.array[j], controller_string_wait_length) == F_equal_to) {
-                action->code = controller_entry_rule_code_wait;
+                action->code |= controller_entry_rule_code_wait;
               }
               else {
                 if (action->status == F_none) {
index 1db153f4f3c91bd87c2aa2e2b5d79c7c6846d81b..90d57444a35fdb858056e24b93224431893eba74 100644 (file)
@@ -13,6 +13,18 @@ extern "C" {
 #endif
 
 /**
+ * Print all parameters for some action, separated by a space.
+ *
+ * @param stream
+ *   The file stream to print to.
+ * @param action
+ *   The entry action whose parameters will be printed.
+ */
+#ifndef _di_controller_entry_action_parameters_print_
+  extern void controller_entry_action_parameters_print(FILE * const stream, const controller_entry_action_t action) f_gcc_attribute_visibility_internal;
+#endif // _di_controller_entry_action_parameters_print_
+
+/**
  * Get a string representing the entry action type.
  *
  * @param type
index 459c07ab7f3255382cbda9874fb729768e3ed11a..a86f4bfb822550b37d1c80b7ac812ec103135f67 100644 (file)
@@ -728,7 +728,7 @@ extern "C" {
 #endif // _di_controller_rule_path_
 
 #ifndef _di_controller_rule_process_
-  f_return_status controller_rule_process(const controller_data_t data, const f_array_length_t index, const bool simulate, controller_setting_t *setting, controller_cache_t *cache) {
+  f_return_status controller_rule_process(const controller_data_t data, const f_array_length_t index, const uint8_t options, controller_setting_t *setting, controller_cache_t *cache) {
 
     if (index >= setting->rules.used) {
       fll_error_print(data.error, F_parameter, "controller_rule_process", F_true);
@@ -817,8 +817,8 @@ extern "C" {
 
     controller_rule_t *rule = &setting->rules.array[index];
 
-    if (simulate && data.parameters[controller_parameter_validate].result == f_console_result_found) {
-      controller_rule_simulate(data, *cache, index, setting);
+    if ((options & controller_rule_option_simulate) && data.parameters[controller_parameter_validate].result == f_console_result_found) {
+      controller_rule_simulate(data, *cache, index, options, setting);
     }
 
     {
@@ -850,7 +850,7 @@ extern "C" {
               status = F_status_set_error(F_found_not);
               controller_rule_error_print(data.error, *cache, F_true);
 
-              if (!simulate) break;
+              if (!(options & controller_rule_option_simulate)) break;
             }
             else {
               if (data.warning.verbosity == f_console_verbosity_debug) {
@@ -895,7 +895,7 @@ extern "C" {
               memcpy(cache_name_file, cache->name_file.string, cache->name_file.used);
 
               // @todo: this should pass or use the asynchronous state.
-              status = controller_rule_process(data, at, simulate, setting, cache);
+              status = controller_rule_process(data, at, options, setting, cache);
 
               // restore cache.
               memcpy(cache->name_action.string, cache_name_action, cache_name_action_used);
@@ -918,7 +918,7 @@ extern "C" {
                   controller_rule_error_need_want_wish_print(data.error, strings[i], dynamics[i]->array[j].string, "failed during execution");
                   controller_rule_error_print(data.error, *cache, F_true);
 
-                  if (!simulate || F_status_set_fine(status) == F_memory_not || F_status_set_fine(status) == F_memory_allocation || F_status_set_fine(status) == F_memory_reallocation) {
+                  if (!(options & controller_rule_option_simulate) || F_status_set_fine(status) == F_memory_not || F_status_set_fine(status) == F_memory_allocation || F_status_set_fine(status) == F_memory_reallocation) {
                     break;
                   }
                 }
@@ -938,7 +938,7 @@ extern "C" {
                 status = F_status_set_error(F_found_not);
                 controller_rule_error_print(data.error, *cache, F_true);
 
-                if (!simulate) break;
+                if (!(options & controller_rule_option_simulate)) break;
               }
               else {
                 if (data.warning.verbosity == f_console_verbosity_debug) {
@@ -950,11 +950,11 @@ extern "C" {
           }
         } // for
 
-        if (F_status_is_error(status) && !simulate) break;
+        if (F_status_is_error(status) && !(options & controller_rule_option_simulate)) break;
       } // for
     }
 
-    if (!simulate && F_status_is_error_not(status)) {
+    if (!(options & controller_rule_option_simulate) && F_status_is_error_not(status)) {
       status = controller_rule_execute(data, *cache, index, setting);
 
       if (F_status_is_error(status)) {
@@ -1721,7 +1721,7 @@ extern "C" {
 #endif // _di_controller_rule_setting_read_
 
 #ifndef _di_controller_rule_simulate_
-  void controller_rule_simulate(const controller_data_t data, const controller_cache_t cache, const f_array_length_t index, controller_setting_t *setting) {
+  void controller_rule_simulate(const controller_data_t data, const controller_cache_t cache, const f_array_length_t index, const uint8_t options, controller_setting_t *setting) {
 
     // @todo this needs the "action" in which to perform, such as "start", "stop", "restart", etc..
 
@@ -1731,6 +1731,8 @@ extern "C" {
     fprintf(data.output.stream, "Rule %s%s%s {%c", data.context.set.title.before->string, rule->id.used ? rule->id.string : f_string_empty_s, data.context.set.title.after->string, f_string_eol_s[0]);
     fprintf(data.output.stream, "  %s%s%s %s%c", data.context.set.important.before->string, controller_string_name, data.context.set.important.after->string, rule->name.used ? rule->name.string : f_string_empty_s, f_string_eol_s[0]);
     fprintf(data.output.stream, "  %s%s%s %s%c", data.context.set.important.before->string, controller_string_control_group, data.context.set.important.after->string, rule->control_group.used ? rule->control_group.string : f_string_empty_s, f_string_eol_s[0]);
+    fprintf(data.output.stream, "  %s%s%s %s%c", data.context.set.important.before->string, controller_string_how, data.context.set.important.after->string, options & controller_rule_option_asynchronous ? controller_string_asynchronous : controller_string_synchronous, f_string_eol_s[0]);
+    fprintf(data.output.stream, "  %s%s%s %s%c", data.context.set.important.before->string, controller_string_wait, data.context.set.important.after->string, options & controller_rule_option_wait ? controller_string_yes : controller_string_no, f_string_eol_s[0]);
 
     f_array_length_t i = 0;
 
index 6c7545ba99aec769bb5989f25dd19e5bb5086f41..50454b11abef476a51893372495bf41c91900bb4 100644 (file)
@@ -351,9 +351,11 @@ extern "C" {
  *   The program data.
  * @param index
  *   Position in the rules array representing the rule to execute
- * @param simulate
- *   If TRUE, then the rule execution is simulated (printing a message that the rule would be executed but does not execut the rule).
- *   If FALSE, the rule is not simulated and is executed as normal.
+ * @param options
+ *   A number using bits to represent specific boolean options.
+ *   If no bits set, then operate normally in a synchronous manner.
+ *   If bit controller_rule_option_simulate, then the rule execution is in simulation mode (printing a message that the rule would be executed but does not execute the rule).
+ *   If bit controller_rule_option_asynchronous, then run asynchronously.
  * @param setting
  *   The controller settings data.
  * @param cache
@@ -365,7 +367,7 @@ extern "C" {
  *    F_none on success.
  */
 #ifndef _di_controller_rule_process_
-  extern f_return_status controller_rule_process(const controller_data_t data, const f_array_length_t index, const bool simulate, controller_setting_t *setting, controller_cache_t *cache) f_gcc_attribute_visibility_internal;
+  extern f_return_status controller_rule_process(const controller_data_t data, const f_array_length_t index, const uint8_t options, controller_setting_t *setting, controller_cache_t *cache) f_gcc_attribute_visibility_internal;
 #endif // _di_controller_rule_process_
 
 /**
@@ -454,11 +456,16 @@ extern "C" {
  *   A structure for containing and caching relevant data.
  * @param index
  *   The position in the setting.rules array representing the rule to simulate.
+ * @param options
+ *   A number using bits to represent specific boolean options.
+ *   If no bits set, then operate normally in a synchronous manner.
+ *   If bit controller_rule_option_simulate, then the rule execution is in simulation mode (printing a message that the rule would be executed but does not execute the rule).
+ *   If bit controller_rule_option_asynchronous, then run asynchronously.
  * @param setting
  *   The controller settings data.
  */
 #ifndef _di_controller_rule_simulate_
-  extern void controller_rule_simulate(const controller_data_t data, const controller_cache_t cache, const f_array_length_t index, controller_setting_t *setting) f_gcc_attribute_visibility_internal;
+  extern void controller_rule_simulate(const controller_data_t data, const controller_cache_t cache, const f_array_length_t index, const uint8_t options, controller_setting_t *setting) f_gcc_attribute_visibility_internal;
 #endif // _di_controller_rule_simulate_
 
 /**