]> Kevux Git Server - fll/commitdiff
Progress: controller program.
authorKevin Day <thekevinday@gmail.com>
Sun, 6 Dec 2020 23:45:41 +0000 (17:45 -0600)
committerKevin Day <thekevinday@gmail.com>
Mon, 7 Dec 2020 04:37:05 +0000 (22:37 -0600)
Add the daemon mode where controller does not act like an init program.

level_3/controller/c/controller.c
level_3/controller/c/controller.h
level_3/controller/c/private-common.h
level_3/controller/c/private-controller.c
level_3/controller/c/private-controller.h
level_3/controller/c/private-entry.c
level_3/controller/c/private-entry.h
level_3/controller/data/settings/entries/default.entry

index 71fb7250f4a024632cff82c9bc2becc9847f9025..d95d6adb706720b5206bbf1624cb5e7484cdedf3 100644 (file)
@@ -26,11 +26,12 @@ extern "C" {
 
     fprintf(output.stream, "%c", f_string_eol[0]);
 
+    fll_program_print_help_option(output, context, controller_short_daemon, controller_long_daemon, f_console_symbol_short_enable, f_console_symbol_long_enable, "       Run in daemon only mode (do not process the entry).");
     fll_program_print_help_option(output, context, controller_short_interruptable, controller_long_interruptable, f_console_symbol_short_enable, f_console_symbol_long_enable, "Designate that this program can be interrupted.");
     fll_program_print_help_option(output, context, controller_short_pid, controller_long_pid, f_console_symbol_short_enable, f_console_symbol_long_enable, "          Specify a custom pid file path, such as '" controller_path_pid "'.");
     fll_program_print_help_option(output, context, controller_short_settings, controller_long_settings, f_console_symbol_short_enable, f_console_symbol_long_enable, "     Specify a custom settings path, such as '" controller_path_settings "'.");
     fll_program_print_help_option(output, context, controller_short_test, controller_long_test, f_console_symbol_short_enable, f_console_symbol_long_enable, "         Run in test mode, where nothing is actually run (a simulation).");
-    fll_program_print_help_option(output, context, controller_short_validate, controller_long_validate, f_console_symbol_short_enable, f_console_symbol_long_enable, "     Validate the settings (entry and rules).");
+    fll_program_print_help_option(output, context, controller_short_validate, controller_long_validate, f_console_symbol_short_enable, f_console_symbol_long_enable, "     Validate the settings (entry and rules) without running (or simulating).");
 
     fll_program_print_help_usage(output, context, controller_name, "entry");
 
@@ -208,7 +209,7 @@ extern "C" {
       }
     }
 
-    // the pid file is required.
+    // a pid file path is required.
     if (!setting.path_pid.used) {
       status = fl_string_append(controller_path_pid, controller_path_pid_length, &setting.path_pid);
 
@@ -219,43 +220,78 @@ extern "C" {
       }
     }
 
-    if (F_status_is_error_not(status)) {
-      status = controller_entry_read(*data, setting, entry_name, &cache, &setting.entry);
+    if (data->parameters[controller_parameter_daemon].result == f_console_result_found) {
 
-      // @fixme this is temporary and may or may not be used when finished codestorming.
-      if (F_status_is_error(setting.entry.status)) {
-        status = setting.entry.status;
-      }
-    }
+      if (data->parameters[controller_parameter_validate].result == f_console_result_found) {
+        if (data->error.verbosity != f_console_verbosity_quiet) {
+          fprintf(data->error.to.stream, "%c", f_string_eol[0]);
+          fprintf(data->error.to.stream, "%s%sThe parameter '", data->error.context.before->string, data->error.prefix ? data->error.prefix : "");
+          fprintf(data->error.to.stream, "%s%s%s%s%s", data->error.context.after->string, data->error.notable.before->string, f_console_symbol_long_enable, controller_long_validate, data->error.notable.after->string);
+          fprintf(data->error.to.stream, "%s' must not be specified with the parameter '", data->error.context.before->string);
+          fprintf(data->error.to.stream, "%s%s%s%s%s", data->error.context.after->string, data->error.notable.before->string, f_console_symbol_long_enable, controller_long_daemon, data->error.notable.after->string);
+          fprintf(data->error.to.stream, "%s'.%s%c", data->error.context.before->string, data->error.context.after->string, f_string_eol[0]);
+        }
 
-    if (F_status_is_error_not(status)) {
-      status = controller_preprocess_items(*data, &setting, &cache);
-    }
+        status = F_status_set_error(F_parameter);
+      }
 
-    if (F_status_is_error_not(status)) {
+      if (F_status_is_error_not(status)) {
+        setting.ready = controller_setting_ready_done;
 
-      if (data->parameters[controller_parameter_test].result == f_console_result_found || data->parameters[controller_parameter_validate].result == f_console_result_found) {
-        // @todo validate happens first, report and handle validation problems or success.
+        if (f_file_exists(setting.path_pid.string) == F_true) {
+          if (data->error.verbosity != f_console_verbosity_quiet) {
+            fprintf(data->error.to.stream, "%c", f_string_eol[0]);
+            fprintf(data->error.to.stream, "%s%sThe pid file must not already exist.%s%c", data->error.context.before->string, data->error.prefix ? data->error.prefix : "", data->error.context.after->string, f_string_eol[0]);
+          }
 
-        if (data->parameters[controller_parameter_test].result == f_console_result_found) {
-          // @todo after validation succeeds, perform test run.
+          status = F_status_set_error(F_available_not);
+          setting.ready = controller_setting_ready_abort;
         }
+
+        // @todo wait here until told to quit, listening for "control" commands (and listening for signals).
+        // @todo clear cache periodically while waiting.
+        // controller_macro_cache_t_delete_simple(cache);
+      }
+    }
+    else {
+
+      if (F_status_is_error_not(status)) {
+        status = controller_entry_read(*data, setting, entry_name, &cache, &setting.entry);
+      }
+
+      if (F_status_is_error(status)) {
+        setting.ready = controller_setting_ready_fail;
       }
       else {
-        // @todo check to see if the standard pid file exists before attempting to start (when in normal operation mode).
-        // @todo real execution.
+        status = controller_preprocess_entry(*data, &setting, &cache);
 
-        // @todo create pid file but not until "ready", so be sure to add this after pre-processing the entry file.
-        if (setting.ready) {
-          controller_file_pid_create(*data, setting.path_pid);
-        }
+        if (data->parameters[controller_parameter_validate].result == f_console_result_none) {
 
-        // @todo when everything is ready, wait here until told to quit.
-        // @todo clear cache periodically while waiting and no commands.
-        // controller_macro_cache_t_delete_simple(cache);
+          if (f_file_exists(setting.path_pid.string) == F_true) {
+            if (data->error.verbosity != f_console_verbosity_quiet) {
+              fprintf(data->error.to.stream, "%c", f_string_eol[0]);
+              fprintf(data->error.to.stream, "%s%sThe pid file must not already exist.%s%c", data->error.context.before->string, data->error.prefix ? data->error.prefix : "", data->error.context.after->string, f_string_eol[0]);
+            }
+
+            status = F_status_set_error(F_available_not);
+            setting.ready = controller_setting_ready_abort;
+          }
 
-        // @todo on failures that prevent normal execution: trigger failsafe/fallback execution, if defined
-        // @todo otherwise, set ready to controller_setting_ready_done.
+          if (F_status_is_error_not(status)) {
+            status = controller_process_entry(*data, &setting, &cache);
+          }
+
+          if (F_status_is_error(status)) {
+            setting.ready = controller_setting_ready_fail;
+          }
+          else {
+            setting.ready = controller_setting_ready_done;
+
+            // @todo wait here until told to quit, listening for "control" commands (and listening for signals).
+            // @todo clear cache periodically while waiting.
+            // controller_macro_cache_t_delete_simple(cache);
+          }
+        }
       }
     }
 
@@ -273,6 +309,10 @@ extern "C" {
 
     controller_delete_data(data);
 
+    if (setting.ready == controller_setting_ready_fail) {
+      // @todo trigger failsafe/fallback execution, if defined.
+    }
+
     return status;
   }
 #endif // _di_controller_main_
index cb95cd9fa01a31f3bc558da470a7531abfc2de68..1ded520b7d0b6a8357aac3dd4d7f24760348fb1a 100644 (file)
@@ -78,12 +78,14 @@ extern "C" {
   #define controller_path_pid_length      34
   #define controller_path_settings_length 15
 
+  #define controller_short_daemon        "d"
   #define controller_short_interruptable "i"
   #define controller_short_pid           "p"
   #define controller_short_settings      "s"
   #define controller_short_test          "t"
   #define controller_short_validate      "V"
 
+  #define controller_long_daemon        "daemon"
   #define controller_long_interruptable "interruptable"
   #define controller_long_pid           "pid"
   #define controller_long_settings      "settings"
@@ -101,6 +103,7 @@ extern "C" {
     controller_parameter_verbosity_debug,
     controller_parameter_version,
 
+    controller_parameter_daemon,
     controller_parameter_interruptable,
     controller_parameter_pid,
     controller_parameter_settings,
@@ -119,6 +122,7 @@ extern "C" {
       f_console_parameter_t_initialize(f_console_standard_short_verbose, f_console_standard_long_verbose, 0, 0, f_console_type_inverse), \
       f_console_parameter_t_initialize(f_console_standard_short_debug, f_console_standard_long_debug, 0, 0, f_console_type_inverse), \
       f_console_parameter_t_initialize(f_console_standard_short_version, f_console_standard_long_version, 0, 0, f_console_type_inverse), \
+      f_console_parameter_t_initialize(controller_short_daemon, controller_long_daemon, 0, 0, f_console_type_normal), \
       f_console_parameter_t_initialize(controller_short_interruptable, controller_long_interruptable, 0, 0, f_console_type_normal), \
       f_console_parameter_t_initialize(controller_short_pid, controller_long_pid, 0, 1, f_console_type_normal), \
       f_console_parameter_t_initialize(controller_short_settings, controller_long_settings, 0, 1, f_console_type_normal), \
@@ -126,7 +130,7 @@ extern "C" {
       f_console_parameter_t_initialize(controller_short_validate, controller_long_validate, 0, 0, f_console_type_normal), \
     }
 
-  #define controller_total_parameters 14
+  #define controller_total_parameters 15
 #endif // _di_controller_defines_
 
 #ifndef _di_controller_data_t_
index 5d7200a9f539270c3c9c5c41ea8b9108a8a9121a..1e1875e9e3af10b48f68e29a6683fe5537c57e1d 100644 (file)
@@ -431,6 +431,8 @@ extern "C" {
     controller_setting_ready_wait,
     controller_setting_ready_yes,
     controller_setting_ready_done,
+    controller_setting_ready_fail,
+    controller_setting_ready_abort,
   };
 
   typedef struct {
index 3733dc55411e546a3d823afbb42f57f1b2c90ec2..9a3da4889cff1e806a1bb411d09ebdc7808519c0 100644 (file)
@@ -52,6 +52,32 @@ extern "C" {
   }
 #endif // _di_controller_entry_action_type_name_
 
+#ifndef _di_controller_string_dynamic_append_terminated_
+  f_return_status controller_string_dynamic_append_terminated(const f_string_static_t source, f_string_dynamic_t *destination) {
+
+    f_status_t status = fl_string_dynamic_append(source, destination);
+
+    if (F_status_is_error(status)) return status;
+
+    status = fl_string_dynamic_terminate_after(destination);
+
+    return status;
+  }
+#endif // _di_controller_string_dynamic_append_terminated_
+
+#ifndef _di_controller_string_dynamic_partial_append_terminated_
+  f_return_status controller_string_dynamic_partial_append_terminated(const f_string_static_t source, const f_string_range_t range, f_string_dynamic_t *destination) {
+
+    f_status_t status = fl_string_dynamic_partial_append(source, range, destination);
+
+    if (F_status_is_error(status)) return status;
+
+    status = fl_string_dynamic_terminate_after(destination);
+
+    return status;
+  }
+#endif // _di_controller_string_dynamic_partial_append_terminated_
+
 #ifndef _di_controller_file_load_
   f_return_status controller_file_load(const controller_data_t data, const controller_setting_t setting, const f_string_t path_prefix, const f_string_static_t path_name, const f_string_t path_suffix, const f_string_length_t path_prefix_length, const f_string_length_t path_suffix_length, f_string_dynamic_t *path_file, f_string_dynamic_t *buffer) {
     f_status_t status = F_none;
@@ -205,7 +231,11 @@ extern "C" {
       status = fl_conversion_string_to_decimal_unsigned(pid_buffer.string, &number, range);
 
       if (F_status_is_error_not(status) && number == data.pid) {
-        f_file_remove(path_pid.string);
+        status = f_file_remove(path_pid.string);
+
+        if (F_status_is_error(status) && data.warning.verbosity == f_console_verbosity_debug) {
+          fll_error_file_print(data.warning, F_status_set_fine(status), "f_file_remove", F_true, path_pid.string, "delete", fll_error_file_type_file);
+        }
       }
     }
 
@@ -213,8 +243,36 @@ extern "C" {
   }
 #endif // _di_controller_file_pid_delete_
 
-#ifndef _di_controller_preprocess_items_
-  f_return_status controller_preprocess_items(const controller_data_t data, controller_setting_t *setting, controller_cache_t *cache) {
+#ifndef _di_controller_perform_ready_
+  f_return_status controller_perform_ready(const controller_data_t data, controller_setting_t *setting, controller_cache_t *cache) {
+    f_status_t status = F_none;
+
+    // only create pid file when not in validate mode.
+    if (data.parameters[controller_parameter_validate].result == f_console_result_none) {
+
+      status = controller_file_pid_create(data, setting->path_pid);
+
+      // 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) {
+          return status;
+        }
+
+        status = F_none;
+      }
+    }
+
+    return status;
+  }
+#endif // _di_controller_perform_ready_
+
+#ifndef _di_controller_preprocess_entry_
+  f_return_status controller_preprocess_entry(const controller_data_t data, controller_setting_t *setting, controller_cache_t *cache) {
     f_status_t status = F_none;
     f_status_t status2 = F_none;
 
@@ -231,6 +289,10 @@ extern "C" {
     setting->ready = controller_setting_ready_no;
 
     cache->ats.used = 0;
+    cache->line_action = 0;
+    cache->line_item = 0;
+    cache->name_action.used = 0;
+    cache->name_item.used = 0;
 
     status = fl_type_array_lengths_increase_by(controller_default_allocation_step, &cache->ats);
 
@@ -246,19 +308,10 @@ extern "C" {
     cache->line_item = setting->entry.items.array[0].line;
     cache->name_item.used = 0;
 
-    status = fl_string_dynamic_append(setting->entry.items.array[0].name, &cache->name_item);
+    status = controller_string_dynamic_append_terminated(setting->entry.items.array[0].name, &cache->name_item);
 
     if (F_status_is_error(status)) {
-      fll_error_print(data.error, F_status_set_fine(status), "fl_string_dynamic_append", F_true);
-      controller_entry_error_print(data.error, *cache);
-
-      return status;
-    }
-
-    status = fl_string_dynamic_terminate_after(&cache->name_item);
-
-    if (F_status_is_error(status)) {
-      fll_error_print(data.error, F_status_set_fine(status), "fl_string_dynamic_terminate_after", F_true);
+      fll_error_print(data.error, F_status_set_fine(status), "controller_string_dynamic_append_terminated", F_true);
       controller_entry_error_print(data.error, *cache);
 
       return status;
@@ -273,19 +326,10 @@ extern "C" {
         cache->line_action = actions->array[cache->ats.array[at_j]].line;
         cache->name_action.used = 0;
 
-        status2 = fl_string_dynamic_append(controller_entry_action_type_name(actions->array[cache->ats.array[at_j]].type), &cache->name_action);
+        status2 = controller_string_dynamic_append_terminated(controller_entry_action_type_name(actions->array[cache->ats.array[at_j]].type), &cache->name_action);
 
         if (F_status_is_error(status2)) {
-          fll_error_print(data.error, F_status_set_fine(status2), "fl_string_dynamic_append", F_true);
-          controller_entry_error_print(data.error, *cache);
-
-          return status2;
-        }
-
-        status2 = fl_string_dynamic_terminate_after(&cache->name_action);
-
-        if (F_status_is_error(status2)) {
-          fll_error_print(data.error, F_status_set_fine(status2), "fl_string_dynamic_terminate_after", F_true);
+          fll_error_print(data.error, F_status_set_fine(status2), "controller_string_dynamic_append_terminated", F_true);
           controller_entry_error_print(data.error, *cache);
 
           return status2;
@@ -369,19 +413,10 @@ extern "C" {
               cache->name_item.used = 0;
               cache->line_item = setting->entry.items.array[i].line;
 
-              status2 = fl_string_dynamic_append(setting->entry.items.array[i].name, &cache->name_item);
+              status2 = controller_string_dynamic_append_terminated(setting->entry.items.array[i].name, &cache->name_item);
 
               if (F_status_is_error(status2)) {
-                fll_error_print(data.error, F_status_set_fine(status2), "fl_string_dynamic_append", F_true);
-                controller_entry_error_print(data.error, *cache);
-
-                return status2;
-              }
-
-              status2 = fl_string_dynamic_terminate_after(&cache->name_item);
-
-              if (F_status_is_error(status2)) {
-                fll_error_print(data.error, F_status_set_fine(status2), "fl_string_dynamic_terminate_after", F_true);
+                fll_error_print(data.error, F_status_set_fine(status2), "controller_string_dynamic_append_terminated", F_true);
                 controller_entry_error_print(data.error, *cache);
 
                 return status2;
@@ -431,19 +466,10 @@ extern "C" {
         cache->line_item = setting->entry.items.array[cache->ats.array[at_i]].line;
         cache->name_item.used = 0;
 
-        status2 = fl_string_dynamic_append(setting->entry.items.array[cache->ats.array[at_i]].name, &cache->name_item);
+        status2 = controller_string_dynamic_append_terminated(setting->entry.items.array[cache->ats.array[at_i]].name, &cache->name_item);
 
         if (F_status_is_error(status2)) {
-          fll_error_print(data.error, F_status_set_fine(status2), "fl_string_dynamic_append", F_true);
-          controller_entry_error_print(data.error, *cache);
-
-          return status2;
-        }
-
-        status2 = fl_string_dynamic_terminate_after(&cache->name_item);
-
-        if (F_status_is_error(status2)) {
-          fll_error_print(data.error, F_status_set_fine(status2), "fl_string_dynamic_terminate_after", F_true);
+          fll_error_print(data.error, F_status_set_fine(status2), "controller_string_dynamic_append_terminated", F_true);
           controller_entry_error_print(data.error, *cache);
 
           return status2;
@@ -456,15 +482,233 @@ extern "C" {
       setting->ready = controller_setting_ready_yes;
     }
 
+    return status;
+  }
+#endif // _di_controller_preprocess_entry_
+
+#ifndef _di_controller_process_entry_
+  f_return_status controller_process_entry(const controller_data_t data, controller_setting_t *setting, controller_cache_t *cache) {
+    f_status_t status = F_none;
+
+    f_array_length_t i = 0;
+    f_array_length_t j = 0;
+
+    f_array_length_t at_i = 0;
+    f_array_length_t at_j = 1;
+
+    controller_entry_actions_t *actions = 0;
+
+    const bool simulate = data.parameters[controller_parameter_test].result == f_console_result_found;
+
     cache->ats.used = 0;
     cache->line_action = 0;
     cache->line_item = 0;
     cache->name_action.used = 0;
     cache->name_item.used = 0;
 
+    if (setting->ready == controller_setting_ready_yes) {
+      status = controller_perform_ready(data, setting, cache);
+      if (F_status_is_error(status)) return status;
+    }
+
+    status = fl_type_array_lengths_increase_by(controller_default_allocation_step, &cache->ats);
+
+    if (F_status_is_error(status)) {
+      fll_error_print(data.error, F_status_set_fine(status), "fl_type_array_lengths_increase_by", F_true);
+      controller_entry_error_print(data.error, *cache);
+
+      return status;
+    }
+
+    cache->ats.array[0] = 0;
+    cache->ats.array[1] = 0;
+    cache->ats.used = 2;
+
+    cache->line_item = setting->entry.items.array[0].line;
+    cache->name_item.used = 0;
+
+    status = controller_string_dynamic_append_terminated(setting->entry.items.array[0].name, &cache->name_item);
+
+    if (F_status_is_error(status)) {
+      fll_error_print(data.error, F_status_set_fine(status), "controller_string_dynamic_append_terminated", F_true);
+      controller_entry_error_print(data.error, *cache);
+
+      return status;
+    }
+
+    for (;;) {
+
+      actions = &setting->entry.items.array[cache->ats.array[at_i]].actions;
+
+      for (; cache->ats.array[at_j] < actions->used; ++cache->ats.array[at_j]) {
+
+        cache->line_action = actions->array[cache->ats.array[at_j]].line;
+        cache->name_action.used = 0;
+
+        status = controller_string_dynamic_append_terminated(controller_entry_action_type_name(actions->array[cache->ats.array[at_j]].type), &cache->name_action);
+
+        if (F_status_is_error(status)) {
+          fll_error_print(data.error, F_status_set_fine(status), "controller_string_dynamic_append_terminated", F_true);
+          controller_entry_error_print(data.error, *cache);
+
+          return status;
+        }
+
+        if (actions->array[cache->ats.array[at_j]].type == controller_entry_action_type_ready) {
+
+          if (setting->ready == controller_setting_ready_wait) {
+            setting->ready = controller_setting_ready_yes;
+
+            controller_perform_ready(data, setting, cache);
+            if (F_status_is_error(status)) return status;
+          }
+        }
+        else if (actions->array[cache->ats.array[at_j]].type == controller_entry_action_type_item) {
+
+          if (actions->array[cache->ats.array[at_j]].number == 0 || actions->array[cache->ats.array[at_j]].number >= setting->entry.items.used) {
+
+            // This should not happen if the pre-process is working as designed, but in case it doesn't, return a critical error to prevent infinite recursion and similar errors.
+            if (data.error.verbosity != f_console_verbosity_quiet) {
+              fprintf(data.error.to.stream, "%c", f_string_eol[0]);
+              fprintf(data.error.to.stream, "%s%sInvalid entry item index ", data.error.context.before->string, data.error.prefix ? data.error.prefix : "");
+              fprintf(data.error.to.stream, "%s%s%llu%s", data.error.context.after->string, data.error.notable.before->string, actions->array[cache->ats.array[at_j]].number, data.error.notable.after->string);
+              fprintf(data.error.to.stream, "%s detected.%s%c", data.error.context.before->string, data.error.context.after->string, f_string_eol[0]);
+            }
+
+            controller_entry_error_print(data.error, *cache);
+
+            return F_status_is_error(F_critical);
+          }
+
+          status = fl_type_array_lengths_increase_by(controller_default_allocation_step, &cache->ats);
+
+          if (F_status_is_error(status)) {
+            fll_error_print(data.error, F_status_set_fine(status), "fl_type_array_lengths_increase_by", F_true);
+            controller_entry_error_print(data.error, *cache);
+
+            return status;
+          }
+
+          // continue into the requested item.
+          at_i = cache->ats.used;
+          at_j = cache->ats.used + 1;
+
+          cache->ats.array[at_i] = actions->array[cache->ats.array[at_j]].number;
+          cache->ats.array[at_j] = 0;
+          cache->ats.used += 2;
+
+          cache->name_action.used = 0;
+          cache->line_action = 0;
+
+          cache->name_item.used = 0;
+          cache->line_item = setting->entry.items.array[actions->array[cache->ats.array[at_j]].number].line;
+
+          status = controller_string_dynamic_append_terminated(setting->entry.items.array[actions->array[cache->ats.array[at_j]].number].name, &cache->name_item);
+
+          if (F_status_is_error(status)) {
+            fll_error_print(data.error, F_status_set_fine(status), "controller_string_dynamic_append_terminated", F_true);
+            controller_entry_error_print(data.error, *cache);
+
+            return status;
+          }
+
+          break;
+        }
+        else if (actions->array[cache->ats.array[at_j]].type == controller_entry_action_type_consider || actions->array[cache->ats.array[at_j]].type == controller_entry_action_type_rule) {
+
+          // 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;
+
+          char cache_name_item[cache_name_action_used];
+          char cache_name_action[cache_name_item_used];
+
+          memcpy(cache_name_item, cache->name_item.string, cache->name_item.used);
+          memcpy(cache_name_action, cache->name_action.string, cache->name_action.used);
+
+          const f_string_length_t rule_id_length = actions->array[cache->ats.array[at_j]].parameters.array[0].used + actions->array[cache->ats.array[at_j]].parameters.array[1].used + 1;
+          char rule_id_name[rule_id_length + 1];
+          const f_string_static_t rule_id = f_macro_string_static_t_initialize(rule_id_name, rule_id_length);
+
+          memcpy(rule_id_name, actions->array[cache->ats.array[at_j]].parameters.array[0].string, actions->array[cache->ats.array[at_j]].parameters.array[0].used);
+          memcpy(rule_id_name + actions->array[cache->ats.array[at_j]].parameters.array[0].used + 1, actions->array[cache->ats.array[at_j]].parameters.array[1].string, actions->array[cache->ats.array[at_j]].parameters.array[1].used);
+
+          rule_id_name[actions->array[cache->ats.array[at_j]].parameters.array[0].used] = f_path_separator[0];
+          rule_id_name[rule_id_length] = 0;
+
+          // @todo check if the rule is already loaded by using the "rule_id", if not then load it.
+
+          //status = controller_rule_read(data, *setting, rule_id, cache, &setting->rules.array[setting->rules.used]);
+
+          // @todo execute rule.
+          if (F_status_is_error_not(status) && actions->array[cache->ats.array[at_j]].type == controller_entry_action_type_rule) {
+            //setting->rules.used++;
+            //status = controller_rule_process();
+          }
+
+          // 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);
+
+          cache->name_action.string[cache_name_action_used] = 0;
+          cache->name_item.string[cache_name_item_used] = 0;
+
+          cache->name_action.used = cache_name_action_used;
+          cache->name_item.used = cache_name_item_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);
+
+            if (!simulate) break;
+          }
+        }
+        else if (actions->array[cache->ats.array[at_j]].type == controller_entry_action_type_timeout) {
+          // @todo
+        }
+        else if (actions->array[cache->ats.array[at_j]].type == controller_entry_action_type_failsafe) {
+          // @todo
+        }
+
+      } // for
+
+      cache->line_action = 0;
+      cache->name_action.used = 0;
+
+      // end of actions found, so drop to previous loop in stack.
+      if (cache->ats.array[at_j] == actions->used) {
+
+        // all actions for "main" are processed so there is nothing left to do.
+        if (at_i == 0) break;
+
+        at_i -= 2;
+        at_j -= 2;
+
+        cache->ats.used -= 2;
+        cache->ats.array[at_j]++;
+
+        cache->line_item = setting->entry.items.array[cache->ats.array[at_i]].line;
+        cache->name_item.used = 0;
+
+        status = controller_string_dynamic_append_terminated(setting->entry.items.array[cache->ats.array[at_i]].name, &cache->name_item);
+
+        if (F_status_is_error(status)) {
+          fll_error_print(data.error, F_status_set_fine(status), "controller_string_dynamic_append_terminated", F_true);
+          controller_entry_error_print(data.error, *cache);
+
+          return status;
+        }
+      }
+    } // for
+
     return status;
   }
-#endif // _di_controller_preprocess_items_
+#endif // _di_controller_process_entry_
 
 #ifndef _di_controller_status_simplify_
   f_return_status controller_status_simplify(const f_status_t status) {
@@ -489,7 +733,7 @@ extern "C" {
       return F_status_set_error(F_number);
     }
 
-    if (status == F_parameter || status == F_found_not || status == F_interrupt || status == F_supported_not) {
+    if (status == F_parameter || status == F_found_not || status == F_interrupt || status == F_supported_not || status == F_critical) {
       return F_status_set_error(status);
     }
 
index 4615ce1e6a8f34b3746b16dbc4e85a171277cc58..e7058c0489c4fd96b622a7173e58ad526ef60e43 100644 (file)
@@ -11,6 +11,7 @@
 #ifdef __cplusplus
 extern "C" {
 #endif
+
 /**
  * Get a string representing the entry action type.
  *
@@ -26,6 +27,50 @@ extern "C" {
 #endif // _di_controller_entry_action_type_name_
 
 /**
+ * Append a string and then add a NULL after the end of the string.
+ *
+ * @param source
+ *   The string to copy from.
+ * @param destination
+ *   The string to copy to.
+ *
+ * @return
+ *   F_none on success.
+ *
+ *   Errors (with error bit) from: fl_string_dynamic_append().
+ *   Errors (with error bit) from: fl_string_dynamic_terminate_after().
+ *
+ * @see fl_string_dynamic_append()
+ * @see fl_string_dynamic_terminate_after()
+ */
+#ifndef _di_controller_string_dynamic_append_terminated_
+  extern f_return_status controller_string_dynamic_append_terminated(const f_string_static_t from, f_string_dynamic_t *destination) f_gcc_attribute_visibility_internal;
+#endif // _di_controller_string_dynamic_append_terminated_
+
+/**
+ * Append given range from within a string and then add a NULL after the end of the string.
+ *
+ * @param from
+ *   The string to copy from.
+ * @param range
+ *   The range within the from string to copy.
+ * @param destination
+ *   The string to copy to.
+ *
+ * @return
+ *   F_none on success.
+ *
+ *   Errors (with error bit) from: fl_string_dynamic_append().
+ *   Errors (with error bit) from: fl_string_dynamic_terminate_after().
+ *
+ * @see fl_string_dynamic_append()
+ * @see fl_string_dynamic_terminate_after()
+ */
+#ifndef _di_controller_string_dynamic_partial_append_terminated_
+  extern f_return_status controller_string_dynamic_partial_append_terminated(const f_string_static_t from, const f_string_range_t range, f_string_dynamic_t *destination) f_gcc_attribute_visibility_internal;
+#endif // _di_controller_string_dynamic_partial_append_terminated_
+
+/**
  * Load a file from the controller settings directory.
  *
  * @param data
@@ -100,7 +145,9 @@ extern "C" {
 #endif // _di_controller_file_pid_delete_
 
 /**
- * Load relevant rules into memory and performing other pre-process tasks.
+ * Perform all activities requiring the state to be "ready".
+ *
+ * This prints messages on errors.
  *
  * @param data
  *   The program data.
@@ -111,10 +158,70 @@ extern "C" {
  *
  * @return
  *   F_none on success.
+ *
+ *   Errors from controller_file_pid_create() are not returned, unless it is a memory error.
+ *
+ * @see controller_file_pid_create()
+ */
+#ifndef _di_controller_perform_ready_
+  extern f_return_status controller_perform_ready(const controller_data_t data, controller_setting_t *setting, controller_cache_t *cache) f_gcc_attribute_visibility_internal;
+#endif // _di_controller_perform_ready_
+
+/**
+ * Pre-process all items for the loaded entry.
+ *
+ * @param data
+ *   The program data.
+ * @param setting
+ *   The controller settings data.
+ * @param cache
+ *   The cache.
+ *
+ * @return
+ *   F_none on success.
+ *   F_recurse (with error bit) on a recursion error.
+ *   F_valid_not (with error bit) on invalid entry item, entry item action, or entry item action value.
+ *
+ *   Errors (with error bit) from: fl_string_dynamic_append().
+ *   Errors (with error bit) from: fl_string_dynamic_terminate_after().
+ *   Errors (with error bit) from: fl_type_array_lengths_increase_by().
+ *
+ *   This will detect and report all errors, but only the first error is returned.
+ *   Memory related errors return immediately.
+ *
+ * @see fl_string_dynamic_append()
+ * @see fl_string_dynamic_terminate_after()
+ * @see fl_type_array_lengths_increase_by()
+ */
+#ifndef _di_controller_preprocess_entry_
+  extern f_return_status controller_preprocess_entry(const controller_data_t data, controller_setting_t *setting, controller_cache_t *cache) f_gcc_attribute_visibility_internal;
+#endif // _di_controller_preprocess_entry_
+
+/**
+ * Process (execute) all items for the loaded entry.
+ *
+ * @param data
+ *   The program data.
+ * @param setting
+ *   The controller settings data.
+ * @param cache
+ *   The cache.
+ *
+ * @return
+ *   F_none on success.
+ *   F_critical (with error bit) on any critical error.
+ *
+ *   Errors (with error bit) from: controller_perform_ready().
+ *   Errors (with error bit) from: controller_string_dynamic_append_terminated().
+ *   Errors (with error bit) from: fl_type_array_lengths_increase_by().
+ *
+ * @see controller_perform_ready()
+ * @see controller_string_dynamic_append_terminated()
+ * @see fl_type_array_lengths_increase_by()
  */
-#ifndef _di_controller_preprocess_items_
-  extern f_return_status controller_preprocess_items(const controller_data_t data, controller_setting_t *setting, controller_cache_t *cache) f_gcc_attribute_visibility_internal;
-#endif // _di_controller_preprocess_items_
+#ifndef _di_controller_process_entry_
+  extern f_return_status controller_process_entry(const controller_data_t data, controller_setting_t *setting, controller_cache_t *cache) f_gcc_attribute_visibility_internal;
+#endif // _di_controller_process_entry_
 
 /**
  * Given a wide range of status codes, simplify them down to a small subset.
index 4f56c6a22e6f3b256e79cd10abc15ac4753d0bc6..79a9b51ebe4dd13f8125bd0ea84ef961fb9c6497 100644 (file)
@@ -640,17 +640,10 @@ extern "C" {
             break;
           }
 
-          status = fl_string_dynamic_partial_append_nulless(cache->buffer_file, cache->object_items.array[i], &cache->name_item);
+          status = controller_string_dynamic_partial_append_terminated(cache->buffer_file, cache->object_items.array[i], &cache->name_item);
 
           if (F_status_is_error(status)) {
-            fll_error_print(data.error, F_status_set_fine(status), "fl_string_dynamic_partial_append_nulless", F_true);
-            break;
-          }
-
-          status = fl_string_dynamic_terminate_after(&cache->name_item);
-
-          if (F_status_is_error(status)) {
-            fll_error_print(data.error, F_status_set_fine(status), "fl_string_dynamic_terminate_after", F_true);
+            fll_error_print(data.error, F_status_set_fine(status), "controller_string_dynamic_partial_append_terminated", F_true);
             break;
           }
 
@@ -707,17 +700,10 @@ extern "C" {
 
           entry->items.array[at].line = cache->line_item;
 
-          status = fl_string_dynamic_append(cache->name_item, &entry->items.array[at].name);
+          status = controller_string_dynamic_append_terminated(cache->name_item, &entry->items.array[at].name);
 
           if (F_status_is_error(status)) {
-            fll_error_print(data.error, F_status_set_fine(status), "fl_string_dynamic_append", F_true);
-            break;
-          }
-
-          status = fl_string_dynamic_terminate_after(&entry->items.array[at].name);
-
-          if (F_status_is_error(status)) {
-            fll_error_print(data.error, F_status_set_fine(status), "fl_string_dynamic_terminate_after", F_true);
+            fll_error_print(data.error, F_status_set_fine(status), "controller_string_dynamic_append_terminated", F_true);
             break;
           }
 
@@ -784,31 +770,10 @@ extern "C" {
                     cache->line_action = action->line;
                     cache->line_item = entry->items.array[i].line;
 
-                    status = fl_string_dynamic_append(entry->items.array[i].name, &cache->name_item);
+                    status = controller_string_dynamic_append_terminated(entry->items.array[i].name, &cache->name_item);
 
                     if (F_status_is_error(status)) {
-                      fll_error_print(data.error, F_status_set_fine(status), "fl_string_dynamic_append", F_true);
-                      break;
-                    }
-
-                    status = fl_string_dynamic_terminate_after(&cache->name_item);
-
-                    if (F_status_is_error(status)) {
-                      fll_error_print(data.error, F_status_set_fine(status), "fl_string_dynamic_terminate_after", F_true);
-                      break;
-                    }
-
-                    status = fl_string_dynamic_append(entry->items.array[i].name, &cache->name_item);
-
-                    if (F_status_is_error(status)) {
-                      fll_error_print(data.error, F_status_set_fine(status), "fl_string_dynamic_append", F_true);
-                      break;
-                    }
-
-                    status = fl_string_dynamic_terminate_after(&cache->name_item);
-
-                    if (F_status_is_error(status)) {
-                      fll_error_print(data.error, F_status_set_fine(status), "fl_string_dynamic_terminate_after", F_true);
+                      fll_error_print(data.error, F_status_set_fine(status), "controller_string_dynamic_append_terminated", F_true);
                       break;
                     }
 
@@ -832,7 +797,7 @@ extern "C" {
 
             // the error is already fully printed and the entry status is already assigned, so immediately exit.
             if (missing & 0x2) {
-              return F_false;
+              return entry->status;
             }
           }
         }
@@ -843,11 +808,12 @@ extern "C" {
       controller_entry_error_print(data.error, *cache);
 
       entry->status = controller_status_simplify(F_status_set_fine(status));
-      return F_false;
+    }
+    else {
+      entry->status = F_none;
     }
 
-    entry->status = F_none;
-    return F_true;
+    return entry->status;
   }
 #endif // _di_controller_entry_read_
 
index 81f8ad56b38c81847744b4cf45e6633fcb59b3f1..3857d20b2c06d9fe4bfc34b27076343c4175a38c 100644 (file)
@@ -129,13 +129,27 @@ extern "C" {
  *   The processed entry.
  *
  * @return
- *   F_true on success.
- *   F_false on failure.
+ *   F_none on success.
+ *
+ *   Errors (with error bit) from: controller_entry_actions_read().
+ *   Errors (with error bit) from: controller_entry_items_increase_by().
+ *   Errors (with error bit) from: controller_file_load().
+ *   Errors (with error bit) from: controller_status_simplify().
+ *   Errors (with error bit) from: controller_string_dynamic_append_terminated().
+ *   Errors (with error bit) from: controller_string_dynamic_partial_append_terminated().
+ *   Errors (with error bit) from: f_fss_count_lines().
+ *   Errors (with error bit) from: fl_fss_apply_delimit().
+ *   Errors (with error bit) from: fl_string_dynamic_append().
+ *   Errors (with error bit) from: fl_string_dynamic_partial_append_nulless().
+ *   Errors (with error bit) from: fl_string_dynamic_terminate().
+ *   Errors (with error bit) from: fll_fss_basic_list_read().
  *
  * @see controller_entry_actions_read()
  * @see controller_entry_items_increase_by()
  * @see controller_file_load()
  * @see controller_status_simplify()
+ * @see controller_string_dynamic_append_terminated()
+ * @see controller_string_dynamic_partial_append_terminated()
  * @see f_fss_count_lines()
  * @see fl_fss_apply_delimit()
  * @see fl_string_dynamic_append()
index ff056934f07d7ed9f356446bc50625eb7cc44cb4..d6770a29a16a95bb1c1346882313512507f4e949 100644 (file)
@@ -8,14 +8,14 @@ main:
   timeout stop 7
   timeout kill 3
 
+  failsafe maintenance
+
   item boot
   item net
   item time
   item keyboard
   item console
 
-  failsafe maintenance
-
 boot:
   rule boot filesystem require
   rule boot modules require