]> Kevux Git Server - fll/commitdiff
Progress: redesign processing logic to accommodate different process Rule Actions...
authorKevin Day <thekevinday@gmail.com>
Fri, 23 Apr 2021 01:49:21 +0000 (20:49 -0500)
committerKevin Day <thekevinday@gmail.com>
Fri, 23 Apr 2021 03:25:42 +0000 (22:25 -0500)
When I implemented the "exit" support (opposite of an "entry") I noticed a oversight in the design whereas there was no way to distinguish between a process that successfully started via the "entry" or the "exit".
Change the design to now utilize unique Rule Processes for each Rule Action requested.

This required further changes to the status handling.
A rule status is now an array of all possible Rule Actions.
This is utilized using a static array for simplicity purposes (there is no need for a dynamic array here).

All of the recent changes introduced a lot more complex code.
There are now helper functions to help facility common tasks.
This should also make updating easier as there is only one place to update.
The downside is the introduction of an additional function call (which is a tiny runtime cost).

To facility this new design, the Rule files must also be aware of the different Rule Actions.
The "need", "want", and "wish" have been relocated into a new Rule Action called "on".
Additional parameters for an "on" allow for describing the Rule Action in which the dependency applies to.
This allows, for example, a "stop" Action to operate in a different order than a "start" Action.
An example of this is provided.
Look at the data/settings/example/rules/serial/*.rule files.

An example syntax is:
  on start need serial s_1
  on stop need serial s_3

When validate is passed, do not wait for asynchronous processes because they are not run.
Normally this is not noticeable but is exposed when 'script/fail' failed to execute and return ('script/fail' should execute, fail, and return).
This bug is caused by the wait functions not checking to see if the caller is the same as the current process (it was waiting for itself).
This may have been introduced as a result of the redesign.

26 files changed:
level_3/controller/c/private-common.c
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-rule.c
level_3/controller/c/private-rule.h
level_3/controller/data/settings/example/exits/serial.exit
level_3/controller/data/settings/example/rules/asynchronous/sleep_1.rule
level_3/controller/data/settings/example/rules/asynchronous/sleep_2.rule
level_3/controller/data/settings/example/rules/asynchronous/sleep_3.rule
level_3/controller/data/settings/example/rules/command/multiple.rule
level_3/controller/data/settings/example/rules/script/fail.rule
level_3/controller/data/settings/example/rules/serial/s_1.rule
level_3/controller/data/settings/example/rules/serial/s_2.rule
level_3/controller/data/settings/example/rules/serial/s_3.rule
level_3/controller/data/settings/example/rules/serial/s_4.rule
level_3/controller/data/settings/example/rules/serial/s_5.rule
level_3/controller/data/settings/example/rules/serial/s_6.rule
level_3/controller/data/settings/rules/boot/devices.rule
level_3/controller/data/settings/rules/boot/modules.rule
level_3/controller/data/settings/rules/net/loopback.rule
level_3/controller/documents/entry.txt
level_3/controller/documents/exit.txt
level_3/controller/documents/rule.txt
level_3/controller/documents/time.txt
level_3/controller/specifications/rule.txt

index 959d22ab5846c9ab9f54929c01ef747d8112906b..36db682ab0442fb5917863b32a5136dff0084765 100644 (file)
@@ -1,6 +1,7 @@
 #include "controller.h"
 #include "private-common.h"
 #include "private-thread.h"
+#include "private-rule.h"
 
 #ifdef __cplusplus
 extern "C" {
@@ -400,7 +401,7 @@ extern "C" {
         break;
       }
 
-      if (process->rule.status != F_known_not && !(process->state == controller_process_state_active || process->state == controller_process_state_busy)) {
+      if (!controller_rule_status_is_available(process->action, process->rule) && !(process->state == controller_process_state_active || process->state == controller_process_state_busy)) {
         f_thread_unlock(&process->lock);
 
         return F_none;
@@ -504,10 +505,13 @@ extern "C" {
 
         if (F_status_is_error(status)) {
           processs->size = length;
+
           return status;
         }
         else {
-          process->rule.status = F_known_not;
+          for (f_array_length_t i = 0; i < controller_rule_action_type__enum_size; ++i) {
+            process->rule.status[i] = F_known_not;
+          } // for
         }
       } // for
 
@@ -555,9 +559,6 @@ extern "C" {
     f_string_maps_resize(0, &rule->parameter);
 
     f_string_dynamics_resize(0, &rule->environment);
-    f_string_dynamics_resize(0, &rule->need);
-    f_string_dynamics_resize(0, &rule->want);
-    f_string_dynamics_resize(0, &rule->wish);
 
     f_macro_int32s_t_delete_simple(rule->affinity)
     f_macro_control_group_t_delete_simple(rule->control_group)
@@ -568,6 +569,7 @@ extern "C" {
       f_capability_delete(&rule->capability);
     }
 
+    controller_rule_ons_delete_simple(&rule->ons);
     controller_rule_items_delete_simple(&rule->items);
   }
 #endif // _di_controller_rule_delete_simple_
@@ -593,6 +595,73 @@ extern "C" {
   }
 #endif // _di_controller_rule_items_delete_simple_
 
+#ifndef _di_controller_rule_on_delete_simple_
+  void controller_rule_on_delete_simple(controller_rule_on_t *on) {
+
+    f_string_dynamics_resize(0, &on->need);
+    f_string_dynamics_resize(0, &on->want);
+    f_string_dynamics_resize(0, &on->wish);
+  }
+#endif // _di_controller_rule_on_delete_simple_
+
+#ifndef _di_controller_rule_ons_delete_simple_
+  void controller_rule_ons_delete_simple(controller_rule_ons_t *ons) {
+
+    ons->used = ons->size;
+
+    while (ons->used) {
+      controller_rule_on_delete_simple(&ons->array[--ons->used]);
+    } // while
+
+    f_memory_delete(ons->size, sizeof(controller_rule_on_t), (void **) & ons->array);
+    ons->size = 0;
+  }
+#endif // _di_controller_rule_ons_delete_simple_
+
+#ifndef _di_controller_rule_ons_increase_
+  f_status_t controller_rule_ons_increase(controller_rule_ons_t *ons) {
+
+    if (ons->used + 1 > ons->size) {
+      f_array_length_t size = ons->used + controller_default_allocation_step;
+
+      if (size > f_array_length_t_size) {
+        if (ons->used + 1 > f_array_length_t_size) {
+          return F_status_set_error(F_array_too_large);
+        }
+
+        size = f_array_length_t_size;
+      }
+
+      return controller_rule_ons_resize(size, ons);
+    }
+
+    return F_data_not;
+  }
+#endif // _di_controller_rule_ons_increase_
+
+#ifndef _di_controller_rule_ons_resize_
+  f_status_t controller_rule_ons_resize(const f_array_length_t length, controller_rule_ons_t *ons) {
+
+    f_status_t status = F_none;
+
+    for (f_array_length_t i = length; i < ons->size; ++i) {
+      controller_rule_on_delete_simple(&ons->array[i]);
+    } // for
+
+    status = f_memory_resize(ons->size, length, sizeof(controller_rule_on_t), (void **) & ons->array);
+
+    if (F_status_is_error_not(status)) {
+      ons->size = length;
+
+      if (ons->used > ons->size) {
+        ons->used = length;
+      }
+    }
+
+    return status;
+  }
+#endif // _di_controller_rule_ons_resize_
+
 #ifndef _di_controller_rules_delete_simple_
   void controller_rules_delete_simple(controller_rules_t *rules) {
 
index 2b7bb517d02a0d7b9cd4d8874f74633558c5f516..a6656a35d1216708fcb031de577fc6debc46df02 100644 (file)
@@ -69,6 +69,7 @@ extern "C" {
   #define controller_string_no            "no"
   #define controller_string_nofile        "nofile"
   #define controller_string_nproc         "nproc"
+  #define controller_string_on            "on"
   #define controller_string_optional      "optional"
   #define controller_string_other         "other"
   #define controller_string_parameter     "parameter"
@@ -161,6 +162,7 @@ extern "C" {
   #define controller_string_no_length            2
   #define controller_string_nofile_length        6
   #define controller_string_nproc_length         5
+  #define controller_string_on_length            2
   #define controller_string_optional_length      8
   #define controller_string_other_length         5
   #define controller_string_parameter_length     9
@@ -254,6 +256,7 @@ extern "C" {
   const static f_string_t controller_string_no_s = controller_string_no;
   const static f_string_t controller_string_nofile_s = controller_string_nofile;
   const static f_string_t controller_string_nproc_s = controller_string_nproc;
+  const static f_string_t controller_string_on_s = controller_string_on;
   const static f_string_t controller_string_optional_s = controller_string_optional;
   const static f_string_t controller_string_other_s = controller_string_other;
   const static f_string_t controller_string_parameter_s = controller_string_parameter;
@@ -486,6 +489,25 @@ extern "C" {
 /**
  * A Rule Action.
  *
+ * controller_rule_action_method_*:
+ *   - extended:      Designate that this Action is represented using FSS Extended.
+ *   - extended_list: Designate that this Action is represented using FSS Extended List.
+ *
+ * controller_rule_action_type_*:
+ *   - freeze:   The Freeze execution instructions.
+ *   - group:    The Group setting.
+ *   - kill:     The Kill execution instructions.
+ *   - pause:    The Pause execution instructions.
+ *   - pid_file: The PID file setting.
+ *   - reload:   The Reload execution instructions.
+ *   - restart:  The Restart execution instructions.
+ *   - resume:   The Resume execution instructions.
+ *   - start:    The Start execution instructions.
+ *   - stop:     The Stop execution instructions.
+ *   - thaw:     The Thaw execution instructions.
+ *   - user:     The User setting.
+ *   - with:     The With flags.
+ *
  * type:       The Rule Action type.
  * line:       The line number where the Rule Action begins.
  * status:     The last execution status of the Rule Action.
@@ -517,6 +539,9 @@ extern "C" {
     controller_rule_action_type_thaw,
     controller_rule_action_type_user,
     controller_rule_action_type_with,
+
+    // designate the largest value in the enum, the '__' is intended.
+    controller_rule_action_type__enum_size,
   };
 
   typedef struct {
@@ -560,6 +585,13 @@ extern "C" {
 /**
  * A Rule Item.
  *
+ * controller_rule_item_type_*:
+ *   - command: A Command to execute.
+ *   - script:  A Script to execute.
+ *   - service: A Service to execute.
+ *   - setting: Settings associated with the Rule Item.
+ *   - utility: A Utility to execute.
+ *
  * type:    The type of the Rule Item.
  * line:    The line number where the Rule Item begins.
  * actions: The actions associated with the Rule Item.
@@ -611,35 +643,103 @@ extern "C" {
 #endif // _di_controller_rule_items_t_
 
 /**
+ * The Rule "on" values for designating dependencies.
+ *
+ * action: The Rule Action type this "on" dependencies are associated with.
+ * need:   The Rule Alias for a required Rule.
+ * want:   The Rule Alias for an optional Rule that is required to succeed if found.
+ * wish:   The Rule Alias for an optional Rule that is not required.
+ */
+#ifndef _di_controller_rule_on_t_
+  typedef struct {
+    uint8_t action;
+
+    f_string_dynamics_t need;
+    f_string_dynamics_t want;
+    f_string_dynamics_t wish;
+  } controller_rule_on_t;
+
+  #define controller_rule_on_initialize { \
+    0, \
+    f_string_dynamics_t_initialize, \
+    f_string_dynamics_t_initialize, \
+    f_string_dynamics_t_initialize, \
+  }
+#endif // _di_controller_rule_on_t_
+
+/**
+ * The Rule "on" array.
+ *
+ * array: An array of Rule "on" values.
+ * size:  Total amount of allocated space.
+ * used:  Total number of allocated spaces used.
+ */
+#ifndef _di_controller_rule_ons_t_
+  typedef struct {
+    controller_rule_on_t *array;
+
+    f_array_length_t size;
+    f_array_length_t used;
+  } controller_rule_ons_t;
+
+  #define controller_rule_ons_t_initialize { \
+    0, \
+    0, \
+    0, \
+  }
+#endif // _di_controller_rule_ons_t_
+
+/**
  * A Rule.
  *
- * status:           The status of the rule as the result of processing/execution.
- * timeout_kill:     The timeout to wait relating to using a kill signal.
- * timeout_start:    The timeout to wait relating to starting a process.
- * timeout_stop:     The timeout to wait relating to stopping a process.
- * status:           A status associated with the loading of the rule (not the execution of the rule).
- * has:              Bitwise set of "has" codes representing what the Rule has.
- * nice:             The niceness value if the Rule "has" nice.
- * user:             The User ID if the Rule "has" a user.
- * group:            The Group ID if the Rule "has" a group.
- * timestamp:        The timestamp when the Rule was loaded.
- * id:               The distinct ID (machine name) of the rule, such as "service/ssh".
- * name:             A human name for the Rule (does not have to be distinct).
- * path:             The path to the Rule file.
- * script:           The program or path to the program of the scripting engine to use when processing scripts in this Rule.
- * define:           Any defines (environment variables) made available to the Rule for IKI substitution or just as environment variables.
- * parameters:       Any parameters made available to the Rule for IKI substitution.
- * environment:      All environment variables allowed to be exposed to the Rule when processing.
- * need:             A Rule ID (machine name) of the Rule that is needed (required).
- * want:             A Rule ID (machine name) of the Rule that is wanted (optional).
- * wish:             A Rule ID (machine name) of the Rule that is wished for (optional).
- * affinity:         The cpu affinity to be used when executing the Rule.
- * capability:       The capability setting if the Rule "has" a capability.
- * control_group:    The control group setting if the Rule "has" a control group.
- * groups:           The groups to assign to the user to run as (with the first group being the primary group).
- * limits:           The cpu/resource limits to use when executing the Rule.
- * scheduler:        The scheduler setting if the Rule "has" a scheduler.
- * items:            All items associated with the Rule.
+ * controller_rule_setting_type_*:
+ *   - affinity:      Setting type representing a affinity.
+ *   - capability:    Setting type representing a capability.
+ *   - control_group: Setting type representing a control group.
+ *   - define:        Setting type representing a define.
+ *   - environment:   Setting type representing a environment.
+ *   - group:         Setting type representing a group.
+ *   - limit:         Setting type representing a limit.
+ *   - name:          Setting type representing a name.
+ *   - nice:          Setting type representing a nice.
+ *   - on:            Setting type representing a on.
+ *   - parameter:     Setting type representing a parameter.
+ *   - path:          Setting type representing a path.
+ *   - scheduler:     Setting type representing a scheduler.
+ *   - script:        Setting type representing a script.
+ *   - user:          Setting type representing a user.
+ *
+ * controller_rule_has_*:
+ *   - control_group: Has type representing a control group.
+ *   - group:         Has type representing a group.
+ *   - nice:          Has type representing a nice.
+ *   - scheduler:     Has type representing a scheduler.
+ *   - user:          Has type representing a user.
+ *
+ * affinity:      The cpu affinity to be used when executing the Rule.
+ * alias:         The distinct ID (machine name) of the rule, such as "service/ssh".
+ * capability:    The capability setting if the Rule "has" a capability.
+ * control_group: The control group setting if the Rule "has" a control group.
+ * define:        Any defines (environment variables) made available to the Rule for IKI substitution or just as environment variables.
+ * environment:   All environment variables allowed to be exposed to the Rule when processing.
+ * group:         The group ID if the Rule "has" a group.
+ * groups:        A set of group IDs to run the process with (first specified group is the primary group).
+ * has:           Bitwise set of "has" codes representing what the Rule has.
+ * items:         All items associated with the Rule.
+ * limits:        The cpu/resource limits to use when executing the Rule.
+ * name:          A human name for the Rule (does not have to be distinct), such as "Bash Script".
+ * nice:          The niceness value if the Rule "has" nice.
+ * on:            A set of parameters for defining dependencies and how they are needed, wanted, or wished for.
+ * parameter:     Any parameters made available to the Rule for IKI substitution.
+ * path:          The path to the Rule file.
+ * scheduler:     The scheduler setting if the Rule "has" a scheduler.
+ * script:        The program or path to the program of the scripting engine to use when processing scripts in this Rule.
+ * status:        A set of action-specific success/failure status of the Rule. Each index represents a controller_rule_action_type_* enum value. Index 0 represents a global status.
+ * timeout_kill:  The timeout to wait relating to using a kill signal.
+ * timeout_start: The timeout to wait relating to starting a process.
+ * timeout_stop:  The timeout to wait relating to stopping a process.
+ * timestamp:     The timestamp when the Rule was loaded.
+ * user:          The User ID if the Rule "has" a user.
  */
 #ifndef _di_controller_rule_t_
   enum {
@@ -651,15 +751,13 @@ extern "C" {
     controller_rule_setting_type_group,
     controller_rule_setting_type_limit,
     controller_rule_setting_type_name,
-    controller_rule_setting_type_need,
     controller_rule_setting_type_nice,
+    controller_rule_setting_type_on,
     controller_rule_setting_type_parameter,
     controller_rule_setting_type_path,
     controller_rule_setting_type_scheduler,
     controller_rule_setting_type_script,
     controller_rule_setting_type_user,
-    controller_rule_setting_type_want,
-    controller_rule_setting_type_wish,
   };
 
   // bitwise codes representing properties on controller_rule_t that have been found in the rule file.
@@ -670,7 +768,7 @@ extern "C" {
   #define controller_rule_has_user          0x10
 
   typedef struct {
-    f_status_t status;
+    f_status_t status[controller_rule_action_type__enum_size];
 
     f_number_unsigned_t timeout_kill;
     f_number_unsigned_t timeout_start;
@@ -692,9 +790,6 @@ extern "C" {
     f_string_maps_t parameter;
 
     f_string_dynamics_t environment;
-    f_string_dynamics_t need;
-    f_string_dynamics_t want;
-    f_string_dynamics_t wish;
 
     f_int32s_t affinity;
     f_capability_t capability;
@@ -703,11 +798,27 @@ extern "C" {
     f_limit_sets_t limits;
     f_execute_scheduler_t scheduler;
 
+    controller_rule_ons_t ons;
     controller_rule_items_t items;
   } controller_rule_t;
 
   #define controller_rule_t_initialize { \
-    F_known_not, \
+      { \
+        F_known_not, \
+        F_known_not, \
+        F_known_not, \
+        F_known_not, \
+        F_known_not, \
+        F_known_not, \
+        F_known_not, \
+        F_known_not, \
+        F_known_not, \
+        F_known_not, \
+        F_known_not, \
+        F_known_not, \
+        F_known_not, \
+        F_known_not, \
+      }, \
     0, \
     0, \
     0, \
@@ -723,15 +834,13 @@ extern "C" {
     f_string_maps_t_initialize, \
     f_string_maps_t_initialize, \
     f_string_dynamics_t_initialize, \
-    f_string_dynamics_t_initialize, \
-    f_string_dynamics_t_initialize, \
-    f_string_dynamics_t_initialize, \
     f_int32s_t_initialize, \
     f_capability_t_initialize, \
     f_control_group_t_initialize, \
     f_int32s_t_initialize, \
     f_limit_sets_t_initialize, \
     f_execute_scheduler_t_initialize, \
+    controller_rule_ons_initialize, \
     controller_rule_items_initialize, \
   }
 #endif // _di_controller_rule_t_
@@ -1862,6 +1971,74 @@ extern "C" {
 #endif // _di_controller_rule_items_delete_simple_
 
 /**
+ * Fully deallocate all memory for the given rule item without caring about return status.
+ *
+ * @param on
+ *   The on to deallocate.
+ *
+ * @see f_string_dynamic_resize()
+ */
+#ifndef _di_controller_rule_on_delete_simple_
+  extern void controller_rule_on_delete_simple(controller_rule_on_t *on) f_gcc_attribute_visibility_internal;
+#endif // _di_controller_rule_on_delete_simple_
+
+/**
+ * Fully deallocate all memory for the given rule items without caring about return status.
+ *
+ * @param ons
+ *   The rule_ons to deallocate.
+ *
+ * @see controller_rule_on_delete_simple()
+ * @see f_memory_delete()
+ */
+#ifndef _di_controller_rule_ons_delete_simple_
+  extern void controller_rule_ons_delete_simple(controller_rule_ons_t *ons) f_gcc_attribute_visibility_internal;
+#endif // _di_controller_rule_ons_delete_simple_
+
+/**
+ * Increase the size of the rule array, but only if necessary.
+ *
+ * If the given length is too large for the buffer, then attempt to set max buffer size (f_array_length_t_size).
+ * If already set to the maximum buffer size, then the resize will fail.
+ *
+ * @param ons
+ *   The on array to resize.
+ *
+ * @return
+ *   F_none on success.
+ *   F_data_not on success, but there is no reason to increase size (used + controller_default_allocation_step <= size).
+ *
+ *   F_array_too_large (with error bit) if the new array length is too large.
+ *   F_memory_not (with error bit) on out of memory.
+ *   F_parameter (with error bit) if a parameter is invalid.
+ *
+ * @see controller_rule_ons_resize()
+ */
+#ifndef _di_controller_rule_ons_increase_
+  extern f_status_t controller_rule_ons_increase(controller_rule_ons_t *ons) f_gcc_attribute_visibility_internal;
+#endif // _di_controller_rule_increase_
+
+/**
+ * Resize the rule array.
+ *
+ * @param length
+ *   The new size to use.
+ * @param ons
+ *   The on array to resize.
+ *
+ * @return
+ *   F_none on success.
+ *
+ *   F_memory_not (with error bit) on out of memory.
+ *   F_parameter (with error bit) if a parameter is invalid.
+ *
+ * @see f_memory_resize()
+ */
+#ifndef _di_controller_rule_ons_resize_
+  extern f_status_t controller_rule_ons_resize(const f_array_length_t length, controller_rule_ons_t *ons) f_gcc_attribute_visibility_internal;
+#endif // _di_controller_rule_ons_resize_
+
+/**
  * Fully deallocate all memory for the given rules without caring about return status.
  *
  * @param rules
index 6cfad595ca15a8db1a2048089134343fda66297e..7569fdca9ff65e5b8862198e2b3979344a2dab54 100644 (file)
@@ -304,14 +304,14 @@ extern "C" {
 #endif // _di_controller_file_pid_read_
 
 #ifndef _di_controller_find_process_
-  f_status_t controller_find_process(const f_string_static_t alias, const controller_processs_t processs, f_array_length_t *at) {
+  f_status_t controller_find_process(const f_array_length_t action, const f_string_static_t alias, const controller_processs_t processs, f_array_length_t *at) {
 
     if (!alias.used) return F_none;
     if (!processs.used) return F_false;
 
     for (f_array_length_t i = 0; i < processs.used; ++i) {
 
-      if (processs.array[i] && fl_string_dynamic_compare(alias, processs.array[i]->rule.alias) == F_equal_to) {
+      if (processs.array[i] && processs.array[i]->action == action && fl_string_dynamic_compare(alias, processs.array[i]->rule.alias) == F_equal_to) {
         if (at) *at = i;
         return F_true;
       }
@@ -915,7 +915,9 @@ extern "C" {
               }
             }
 
-            controller_rule_wait_all(is_entry, *main, F_false, process);
+            if (main->data->parameters[controller_parameter_validate].result == f_console_result_none) {
+              controller_rule_wait_all(is_entry, *main, F_false, process);
+            }
           }
 
           if (main->setting->ready == controller_setting_ready_wait) {
@@ -1097,14 +1099,10 @@ extern "C" {
 
             status_lock = controller_lock_write(is_entry, main->thread, &main->thread->lock.rule);
 
-            if (status_lock == F_signal || F_status_is_error(status_lock)) {
-              controller_lock_error_critical_print(main->data->error, F_status_set_fine(status_lock), F_false, main->thread);
-
-              break;
+            if (!(status_lock == F_signal || F_status_is_error(status_lock))) {
+              status = controller_rule_read(is_entry, alias_rule, *main, cache, &main->setting->rules.array[main->setting->rules.used]);
             }
 
-            status = controller_rule_read(is_entry, alias_rule, *main, cache, &main->setting->rules.array[main->setting->rules.used]);
-
             // restore cache.
             memcpy(cache->action.name_action.string, cache_name_action, cache_name_action_used);
             memcpy(cache->action.name_item.string, cache_name_item, cache_name_item_used);
@@ -1121,6 +1119,12 @@ extern "C" {
             cache->action.line_action = cache_line_action;
             cache->action.line_item = cache_line_item;
 
+            if (status_lock == F_signal || F_status_is_error(status_lock)) {
+              controller_lock_error_critical_print(main->data->error, F_status_set_fine(status_lock), F_false, main->thread);
+
+              break;
+            }
+
             if (status == F_signal || !controller_thread_is_enabled(is_entry, main->thread)) {
               f_thread_unlock(&main->thread->lock.rule);
 
@@ -1148,71 +1152,6 @@ extern "C" {
             }
 
             f_thread_unlock(&main->thread->lock.rule);
-
-            // ensure that a process exists for the added rule.
-            if (F_status_is_error_not(status)) {
-              status_lock = controller_lock_read(is_entry, main->thread, &main->thread->lock.process);
-
-              if (status_lock == F_signal || F_status_is_error(status_lock)) {
-                controller_lock_error_critical_print(main->data->error, F_status_set_fine(status_lock), F_true, main->thread);
-
-                break;
-              }
-
-              if (controller_find_process(alias_rule, main->thread->processs, 0) == F_false) {
-                f_thread_unlock(&main->thread->lock.process);
-
-                status_lock = controller_lock_write(is_entry, main->thread, &main->thread->lock.process);
-
-                if (status_lock == F_signal || F_status_is_error(status_lock)) {
-                  controller_lock_error_critical_print(main->data->error, F_status_set_fine(status_lock), F_false, main->thread);
-
-                  break;
-                }
-
-                status = controller_processs_increase(&main->thread->processs);
-
-                if (F_status_is_error(status)) {
-                  controller_entry_error_print(main->data->error, cache->action, F_status_set_fine(status), "controller_processs_increase", F_true, main->thread);
-                }
-                else if (main->thread->processs.array[main->thread->processs.used]) {
-
-                  // only copy the rule alias, as that is all that is needed at this point (the entire rule gets copied prior to executing/processing).
-                  controller_process_t *process = main->thread->processs.array[main->thread->processs.used];
-
-                  status_lock = controller_lock_write(is_entry, main->thread, &process->lock);
-
-                  if (status_lock == F_signal || F_status_is_error(status_lock)) {
-                    controller_lock_error_critical_print(main->data->error, F_status_set_fine(status_lock), F_false, main->thread);
-
-                    f_thread_unlock(&main->thread->lock.process);
-                    break;
-                  }
-
-                  process->rule.alias.used = 0;
-
-                  status = f_string_dynamic_append(alias_rule, &process->rule.alias);
-
-                  if (F_status_is_error(status)) {
-                    controller_entry_error_print(main->data->error, cache->action, F_status_set_fine(status), "f_string_dynamic_append", F_true, main->thread);
-                  }
-                  else {
-                    status = f_string_dynamic_terminate_after(&process->rule.alias);
-
-                    if (F_status_is_error(status)) {
-                      controller_entry_error_print(main->data->error, cache->action, F_status_set_fine(status), "f_string_dynamic_terminate_after", F_true, main->thread);
-                    }
-                    else {
-                      process->id = main->thread->processs.used++;
-                    }
-                  }
-
-                  f_thread_unlock(&process->lock);
-                }
-              }
-
-              f_thread_unlock(&main->thread->lock.process);
-            }
           }
 
           if (F_status_is_error_not(status)) {
@@ -1403,7 +1342,7 @@ extern "C" {
     }
 
     // check to see if any requied processes failed, but do not do this if already operating in failsafe.
-    if (F_status_is_error_not(status) && !failsafe) {
+    if (F_status_is_error_not(status) && !failsafe && main->data->parameters[controller_parameter_validate].result == f_console_result_none) {
       const f_status_t status_wait = controller_rule_wait_all(is_entry, *main, F_true, 0);
 
       if (status_wait == F_signal || F_status_is_error(status_wait)) {
@@ -1430,6 +1369,100 @@ extern "C" {
   }
 #endif // _di_controller_process_entry_
 
+#ifndef _di_controller_process_prepare_
+  f_status_t controller_process_prepare(const bool is_normal, const uint8_t action, const f_string_static_t alias, const controller_main_t main, f_array_length_t *id) {
+
+    f_status_t status = F_none;
+
+    if (status == F_signal || F_status_is_error(status)) {
+      controller_lock_error_critical_print(main.data->error, F_status_set_fine(status), F_true, main.thread);
+
+      return status;
+    }
+
+    if (controller_find_process(action, alias, main.thread->processs, id) == F_false) {
+      f_thread_unlock(&main.thread->lock.process);
+
+      status = controller_lock_write(is_normal, main.thread, &main.thread->lock.process);
+
+      if (status == F_signal || F_status_is_error(status)) {
+        controller_lock_error_critical_print(main.data->error, F_status_set_fine(status), F_false, main.thread);
+
+        const f_status_t status_lock = controller_lock_read(is_normal, main.thread, &main.thread->lock.process);
+
+        if (status_lock == F_signal || F_status_is_error(status_lock)) {
+          return F_status_set_error(F_lock);
+        }
+
+        return status;
+      }
+
+      status = controller_processs_increase(&main.thread->processs);
+
+      if (F_status_is_error_not(status) && main.thread->processs.array[main.thread->processs.used]) {
+
+        controller_process_t *process = main.thread->processs.array[main.thread->processs.used];
+
+        status = controller_lock_write(is_normal, main.thread, &process->lock);
+
+        if (status == F_signal || F_status_is_error(status)) {
+          controller_lock_error_critical_print(main.data->error, F_status_set_fine(status), F_false, main.thread);
+
+          f_thread_unlock(&main.thread->lock.process);
+
+          const f_status_t status_lock = controller_lock_read(is_normal, main.thread, &main.thread->lock.process);
+
+          if (status_lock == F_signal || F_status_is_error(status_lock)) {
+            return F_status_set_error(F_lock);
+          }
+
+          return status;
+        }
+
+        process->action = action;
+        process->rule.alias.used = 0;
+
+        status = f_string_dynamic_append(alias, &process->rule.alias);
+
+        if (F_status_is_error_not(status)) {
+          status = f_string_dynamic_terminate_after(&process->rule.alias);
+
+          if (F_status_is_error_not(status)) {
+            process->id = main.thread->processs.used++;
+            status = F_none;
+
+            if (id) {
+              *id = process->id;
+            }
+          }
+        }
+
+        f_thread_unlock(&process->lock);
+      }
+
+      f_thread_unlock(&main.thread->lock.process);
+
+      const f_status_t status_lock = controller_lock_read(is_normal, main.thread, &main.thread->lock.process);
+
+      if (status_lock == F_signal || F_status_is_error(status_lock)) {
+        return F_status_set_error(F_lock);
+      }
+    }
+    else {
+      status = F_found;
+    }
+
+    return status;
+  }
+#endif // _di_controller_process_prepare_
+
+#ifndef _di_controller_process_prepare_process_type_
+  f_status_t controller_process_prepare_process_type(const uint8_t type, const uint8_t action, const f_string_static_t alias, const controller_main_t main, f_array_length_t *id) {
+
+    return controller_process_prepare(type != controller_process_type_exit, action, alias, main, id);
+  }
+#endif // _di_controller_process_prepare_process_type_
+
 #ifndef _di_controller_status_simplify_error_
   f_status_t controller_status_simplify_error(const f_status_t status) {
 
index 5d13d643e8914eb9b5fe090ebf052666ad1a0a59..b15f320e8b29d86e2784b063afe34f7ce5405730 100644 (file)
@@ -191,13 +191,15 @@ extern "C" {
 #endif // _di_controller_file_pid_read_
 
 /**
- * Find an existing process.
+ * Find an existing process, for the given Rule Action.
  *
  * Do not confuse this with a process in the context of a PID.
  * This is a stucture for the current processing of some rule.
  *
  * This does not do any locking or unlocking for the processs data, be sure to lock appropriately before and after calling this.
  *
+ * @param action
+ *   The Rule Action to find.
  * @param alias
  *   The Rule alias to find.
  * @param processs
@@ -212,7 +214,7 @@ extern "C" {
  *   F_true if there is a process found (address is stored in "at").
  */
 #ifndef _di_controller_find_process_
-  f_status_t controller_find_process(const f_string_static_t alias, const controller_processs_t processs, f_array_length_t *at) f_gcc_attribute_visibility_internal;
+  f_status_t controller_find_process(const f_array_length_t action, const f_string_static_t alias, const controller_processs_t processs, f_array_length_t *at) f_gcc_attribute_visibility_internal;
 #endif // _di_controller_find_process_
 
 /**
@@ -359,6 +361,83 @@ extern "C" {
 #endif // _di_controller_process_entry_
 
 /**
+ * Prepare the process.
+ *
+ * The process is initialized with the process id, the rule alias, and the rule action type.
+ * These are the necessary parts for uniquely identifying the process.
+ *
+ * If a process by the given Rule alias and Rule Action already exists, then nothing is done.
+ *
+ * This requires that a main.thread->lock.process lock be set on process->lock before being called.
+ *
+ * @param is_normal
+ *   If TRUE, then process as if this is a normal operation (entry and control).
+ *   If FALSE, then process as if this is an exit operation.
+ * @param action
+ *   The Rule Action to use.
+ * @param alias
+ *   The Rule alias to use.
+ * @param main
+ *   The main data.
+ * @param id
+ *   (optional) The process ID when found or created.
+ *   Set to NULL to not use.
+ *
+ * @return
+ *   F_none on success.
+ *   F_found on success, but nothing was done because an existing process was found.
+ *
+ *   F_lock (with error bit) if failed to re-establish read lock on main.thread->lock.process while returning.
+ *
+ *   Errors (with error bit) from: f_string_dynamic_append().
+ *   Errors (with error bit) from: f_string_dynamic_terminate_after().
+ *
+ *   Errors (with error bit) from: controller_lock_read().
+ *   Errors (with error bit) from: controller_lock_write().
+ *
+ * @see f_string_dynamic_append()
+ * @see f_string_dynamic_terminate_after()
+ * @see controller_lock_read()
+ * @see controller_lock_write()
+ */
+#ifndef _di_controller_process_prepare_
+  extern f_status_t controller_process_prepare(const bool is_normal, const uint8_t action, const f_string_static_t alias, const controller_main_t main, f_array_length_t *id) f_gcc_attribute_visibility_internal;
+#endif // _di_controller_process_prepare_
+
+/**
+ * Prepare the process for some process type.
+ *
+ * The process is initialized with the process id, the rule alias, and the rule action type.
+ * These are the necessary parts for uniquely identifying the process.
+ *
+ * If a process by the given Rule alias and Rule Action already exists, then nothing is done.
+ *
+ * This requires that a main.thread->lock.process lock be set on process->lock before being called.
+ *
+ * @param type
+ *   The process type to use when checking if thread is enabled.
+ * @param action
+ *   The Rule Action to use.
+ * @param alias
+ *   The Rule alias to use.
+ * @param main
+ *   The main data.
+ * @param id
+ *   (optional) The process ID when found or created.
+ *   Set to NULL to not use.
+ *
+ * @return
+ *   Success from: controller_process_prepare()
+ *
+ *   Errors (with error bit) from: controller_process_prepare().
+ *
+ * @see controller_process_prepare()
+ */
+#ifndef _di_controller_process_prepare_process_type_
+  extern f_status_t controller_process_prepare_process_type(const uint8_t type, const uint8_t action, const f_string_static_t alias, const controller_main_t main, f_array_length_t *id) f_gcc_attribute_visibility_internal;
+#endif // _di_controller_process_prepare_process_type_
+
+/**
  * Given a wide range of status codes (that are errors), simplify them down to a small subset.
  *
  * @param status
index b1ee47b9da87cd4e94a836b93c470887c41874c8..4f03c60aaed48590a3a0578575725452dcc99bbb 100644 (file)
@@ -135,6 +135,11 @@ extern "C" {
     f_string_static_t buffer = f_string_static_t_initialize;
 
     switch (type) {
+      case controller_rule_action_type_freeze:
+        buffer.string = controller_string_freeze_s;
+        buffer.used = controller_string_freeze_length;
+        break;
+
       case controller_rule_action_type_group:
         buffer.string = controller_string_group_s;
         buffer.used = controller_string_group_length;
@@ -145,19 +150,29 @@ extern "C" {
         buffer.used = controller_string_kill_length;
         break;
 
+      case controller_rule_action_type_pause:
+        buffer.string = controller_string_pause_s;
+        buffer.used = controller_string_pause_length;
+        break;
+
       case controller_rule_action_type_pid_file:
         buffer.string = controller_string_pid_file_s;
         buffer.used = controller_string_pid_file_length;
         break;
 
+      case controller_rule_action_type_reload:
+        buffer.string = controller_string_reload_s;
+        buffer.used = controller_string_reload_length;
+        break;
+
       case controller_rule_action_type_restart:
         buffer.string = controller_string_restart_s;
         buffer.used = controller_string_restart_length;
         break;
 
-      case controller_rule_action_type_reload:
-        buffer.string = controller_string_reload_s;
-        buffer.used = controller_string_reload_length;
+      case controller_rule_action_type_resume:
+        buffer.string = controller_string_resume_s;
+        buffer.used = controller_string_resume_length;
         break;
 
       case controller_rule_action_type_start:
@@ -170,6 +185,11 @@ extern "C" {
         buffer.used = controller_string_stop_length;
         break;
 
+      case controller_rule_action_type_thaw:
+        buffer.string = controller_string_thaw_s;
+        buffer.used = controller_string_thaw_length;
+        break;
+
       case controller_rule_action_type_user:
         buffer.string = controller_string_user_s;
         buffer.used = controller_string_user_length;
@@ -444,7 +464,9 @@ extern "C" {
     f_macro_control_group_t_delete_simple(destination->control_group)
     f_capability_delete(&destination->capability);
 
-    destination->status = source.status;
+    for (f_array_length_t i = 0; i < controller_rule_action_type__enum_size; ++i) {
+      destination->status[i] = source.status[i];
+    } // for
 
     destination->timeout_kill = source.timeout_kill;
     destination->timeout_start = source.timeout_start;
@@ -465,11 +487,7 @@ extern "C" {
 
     destination->define.used = 0;
     destination->parameter.used = 0;
-
     destination->environment.used = 0;
-    destination->need.used = 0;
-    destination->want.used = 0;
-    destination->wish.used = 0;
 
     destination->affinity.used = 0;
     destination->groups.used = 0;
@@ -477,6 +495,15 @@ extern "C" {
     destination->scheduler.policy = source.scheduler.policy;
     destination->scheduler.priority = source.scheduler.priority;
 
+    for (f_array_length_t i = 0; i < destination->ons.size; ++i) {
+
+      destination->ons.array[i].action = 0;
+      destination->ons.array[i].need.used = 0;
+      destination->ons.array[i].want.used = 0;
+      destination->ons.array[i].wish.used = 0;
+    } // for
+
+    destination->ons.used = 0;
     destination->items.used = 0;
 
     if (source.alias.used) {
@@ -514,19 +541,39 @@ extern "C" {
       if (F_status_is_error(status)) return status;
     }
 
-    if (source.need.used) {
-      status = f_string_dynamics_append(source.need, &destination->need);
-      if (F_status_is_error(status)) return status;
-    }
+    if (source.ons.used) {
+      if (destination->ons.used < source.ons.used) {
+        status = controller_rule_ons_resize(source.ons.used, &destination->ons);
+        if (F_status_is_error(status)) return status;
+      }
 
-    if (source.want.used) {
-      status = f_string_dynamics_append(source.want, &destination->want);
-      if (F_status_is_error(status)) return status;
-    }
+      for (f_array_length_t i = 0; i < source.ons.used; ++i) {
 
-    if (source.wish.used) {
-      status = f_string_dynamics_append(source.wish, &destination->wish);
-      if (F_status_is_error(status)) return status;
+        destination->ons.array[i].action = source.ons.array[i].action;
+
+        if (source.ons.array[i].need.used) {
+          destination->ons.array[i].need.used = 0;
+
+          status = f_string_dynamics_append(source.ons.array[i].need, &destination->ons.array[i].need);
+          if (F_status_is_error(status)) return status;
+        }
+
+        if (source.ons.array[i].want.used) {
+          destination->ons.array[i].want.used = 0;
+
+          status = f_string_dynamics_append(source.ons.array[i].want, &destination->ons.array[i].want);
+          if (F_status_is_error(status)) return status;
+        }
+
+        if (source.ons.array[i].wish.used) {
+          destination->ons.array[i].wish.used = 0;
+
+          status = f_string_dynamics_append(source.ons.array[i].wish, &destination->ons.array[i].wish);
+          if (F_status_is_error(status)) return status;
+        }
+      } // for
+
+      destination->ons.used = source.ons.used;
     }
 
     if (source.affinity.used) {
@@ -792,7 +839,7 @@ extern "C" {
       execute_set.as.control_group = &process->rule.control_group;
 
       // make sure all required cgroup directories exist.
-      if (process->rule.status == F_known_not) {
+      if (controller_rule_status_is_available(action, process->rule)) {
         status = fll_control_group_prepare(process->rule.control_group);
 
         if (F_status_is_error(status)) {
@@ -1465,6 +1512,20 @@ extern "C" {
   }
 #endif // _di_controller_rule_id_construct_
 
+#ifndef _di_controller_rule_status_is_available_
+  f_status_t controller_rule_status_is_available(const uint8_t action, const controller_rule_t rule) {
+
+    return F_status_is_error_not(rule.status[0]) && rule.status[action] == F_known_not;
+  }
+#endif // _di_controller_rule_status_is_available_
+
+#ifndef _di_controller_rule_status_is_error_
+  f_status_t controller_rule_status_is_error(const uint8_t action, const controller_rule_t rule) {
+
+    return F_status_is_error(rule.status[0]) || F_status_is_error(rule.status[action]);
+  }
+#endif // _di_controller_rule_status_is_error_
+
 #ifndef _di_controller_rule_item_read_
   f_status_t controller_rule_item_read(const controller_main_t main, controller_cache_t *cache, controller_rule_item_t *item) {
 
@@ -1875,21 +1936,32 @@ extern "C" {
 
       uint8_t options_process = 0;
 
-      f_string_dynamics_t * const dynamics[] = {
-        &process->rule.need,
-        &process->rule.want,
-        &process->rule.wish,
-      };
-
-      const f_string_t strings[] = {
+      const f_string_t strings[3] = {
         "needed",
         "wanted",
         "wished for",
       };
 
+      f_string_dynamics_t empty = f_string_dynamics_t_initialize;
+      f_string_dynamics_t *dynamics[3] = { &empty, &empty, &empty };
+
+      if (process->action) {
+
+        for (i = 0; i < process->rule.ons.used; ++i) {
+
+          if (process->rule.ons.array[i].action == process->action) {
+            dynamics[0] = &process->rule.ons.array[i].need;
+            dynamics[1] = &process->rule.ons.array[i].want;
+            dynamics[2] = &process->rule.ons.array[i].wish;
+
+            break;
+          }
+        } // for
+      }
+
       // i==0 is need, i==1 is want, i==2 is wish.
       // loop through all dependencies: wait for depedency, execute dependency, fail due to missing required dependency, or skip unrequired missing dependencies.
-      for (; i < 3 && controller_thread_is_enabled_process(process, main.thread); ++i) {
+      for (i = 0; i < 3 && controller_thread_is_enabled_process(process, main.thread); ++i) {
 
         for (j = 0; j < dynamics[i]->used && controller_thread_is_enabled_process(process, main.thread); ++j) {
 
@@ -1903,7 +1975,31 @@ extern "C" {
             controller_lock_error_critical_print(main.data->error, F_status_set_fine(status_lock), F_true, main.thread);
           }
           else {
-            status = controller_find_process(dynamics[i]->array[j], main.thread->processs, &id_dependency);
+            status = controller_process_prepare_process_type(process->type, process->action, dynamics[i]->array[j], main, &id_dependency);
+
+            if (F_status_is_error(status)) {
+              if (F_status_set_fine(status) == F_lock) {
+                if (!controller_thread_is_enabled_process_type(process->type, main.thread)) {
+                  return F_signal;
+                }
+              }
+              else {
+
+              }
+
+              if (main.data->error.verbosity != f_console_verbosity_quiet) {
+                f_thread_mutex_lock(&main.thread->lock.print);
+
+                controller_rule_item_error_print_rule_not_loaded(main.data->error, dynamics[i]->array[j].string);
+                controller_rule_error_print_cache(main.data->error, process->cache.action, F_false);
+
+                controller_print_unlock_flush(main.data->error.to.stream, &main.thread->lock.print);
+              }
+
+              return status;
+            }
+
+            status = F_true;
           }
 
           if (status == F_true) {
@@ -2012,7 +2108,7 @@ extern "C" {
 
               if (F_status_is_error(status) && !(process->options & controller_process_option_simulate)) break;
 
-              status = dependency->rule.status;
+              status = dependency->rule.status[process->action];
             }
             else {
               status_lock = controller_lock_read_process(process, main.thread, &main.thread->lock.rule);
@@ -2024,7 +2120,7 @@ extern "C" {
 
                 status = status_lock;
               }
-              else if (main.setting->rules.array[id_rule].status == F_known_not) {
+              else if (controller_rule_status_is_available(process->action, main.setting->rules.array[id_rule])) {
                 f_thread_unlock(&main.thread->lock.rule);
                 f_thread_unlock(&dependency->lock);
 
@@ -2075,7 +2171,7 @@ extern "C" {
                 }
               }
               else {
-                status = main.setting->rules.array[id_rule].status;
+                status = main.setting->rules.array[id_rule].status[process->action];
 
                 f_thread_unlock(&main.thread->lock.rule);
                 f_thread_unlock(&dependency->lock);
@@ -2103,7 +2199,7 @@ extern "C" {
 
               status = status_lock;
             }
-            else if (F_status_is_error(main.setting->rules.array[id_rule].status)) {
+            else if (controller_rule_status_is_error(process->action, main.setting->rules.array[id_rule])) {
               f_thread_unlock(&main.thread->lock.rule);
 
               if (i == 0 || i == 1) {
@@ -2157,7 +2253,8 @@ extern "C" {
       return F_signal;
     }
 
-    if ((process->options & controller_process_option_wait) && F_status_is_error_not(status)) {
+    if ((process->options & controller_process_option_wait) && F_status_is_error_not(status) && main.data->parameters[controller_parameter_validate].result == f_console_result_none) {
+
       status_lock = controller_rule_wait_all_process_type(process->type, main, F_false, process);
 
       if (status_lock == F_signal) {
@@ -2211,10 +2308,6 @@ extern "C" {
           return status;
         }
 
-        if (!controller_thread_is_enabled_process(process, main.thread)) {
-          return F_signal;
-        }
-
         if (F_status_is_error(status)) {
           controller_rule_item_error_print(main.data->error, process->cache.action, F_true, main.thread);
         }
@@ -2242,10 +2335,10 @@ extern "C" {
     }
 
     if (F_status_is_error(status)) {
-      process->rule.status = controller_status_simplify_error(F_status_set_fine(status));
+      process->rule.status[process->action] = controller_status_simplify_error(F_status_set_fine(status));
     }
     else {
-      process->rule.status = status;
+      process->rule.status[process->action] = status;
     }
 
     status_lock = controller_lock_write_process(process, main.thread, &main.thread->lock.rule);
@@ -2267,7 +2360,7 @@ extern "C" {
     if (controller_rule_find(process->rule.alias, main.setting->rules, &id_rule) == F_true) {
       controller_rule_t *rule = &main.setting->rules.array[id_rule];
 
-      rule->status = process->rule.status;
+      rule->status[process->action] = process->rule.status[process->action];
 
       f_array_length_t j = 0;
 
@@ -2297,7 +2390,7 @@ extern "C" {
       return F_status_set_error(F_lock);
     }
 
-    return process->rule.status;
+    return process->rule.status[process->action];
   }
 #endif // _di_controller_rule_process_
 
@@ -2323,9 +2416,7 @@ extern "C" {
     {
       f_array_length_t at = 0;
 
-      if (controller_find_process(alias_rule, main.thread->processs, &at) != F_true) {
-        status = F_status_set_error(F_found_not);
-      }
+      status = controller_process_prepare(type != controller_process_type_exit, action, alias_rule, main, &at);
 
       if (F_status_is_error(status)) {
         f_thread_unlock(&main.thread->lock.process);
@@ -2738,10 +2829,10 @@ extern "C" {
 
     if (controller_rule_find(process->rule.alias, main.setting->rules, &id_rule) == F_true) {
       if (F_status_set_fine(status) == F_lock) {
-        main.setting->rules.array[id_rule].status = F_status_set_error(F_failure);
+        main.setting->rules.array[id_rule].status[process->action] = F_status_set_error(F_failure);
       }
       else {
-        main.setting->rules.array[id_rule].status = status;
+        main.setting->rules.array[id_rule].status[process->action] = status;
       }
     }
 
@@ -2800,12 +2891,14 @@ extern "C" {
 #endif // _di_controller_rule_process_do_
 
 #ifndef _di_controller_rule_read_
-  f_status_t controller_rule_read(const bool is_normal, const f_string_static_t rule_id, controller_main_t main, controller_cache_t *cache, controller_rule_t *rule) {
+  f_status_t controller_rule_read(const bool is_normal, const f_string_static_t alias, controller_main_t main, controller_cache_t *cache, controller_rule_t *rule) {
     f_status_t status = F_none;
 
     bool for_item = F_true;
 
-    rule->status = F_known_not;
+    for (f_array_length_t i = 0; i < controller_rule_action_type__enum_size; ++i) {
+      rule->status[i] = F_known_not;
+    } // for
 
     // @todo: timeouts may be passed from entry, consider to or not to initialize in a more consistent manner.
     //rule->timeout_kill = 2;
@@ -2826,11 +2919,7 @@ extern "C" {
 
     rule->define.used = 0;
     rule->parameter.used = 0;
-
     rule->environment.used = 0;
-    rule->need.used = 0;
-    rule->want.used = 0;
-    rule->wish.used = 0;
 
     rule->affinity.used = 0;
 
@@ -2855,6 +2944,13 @@ extern "C" {
     rule->scheduler.policy = 0;
     rule->scheduler.priority = 0;
 
+    for (f_array_length_t i = 0; i < rule->ons.size; ++i) {
+      rule->ons.array[i].need.used = 0;
+      rule->ons.array[i].want.used = 0;
+      rule->ons.array[i].wish.used = 0;
+    } // for
+
+    rule->ons.used = 0;
     rule->items.used = 0;
 
     cache->action.line_item = 0;
@@ -2881,7 +2977,7 @@ extern "C" {
     cache->action.name_file.used = 0;
     cache->action.name_item.used = 0;
 
-    status = f_string_dynamic_append_nulless(rule_id, &rule->alias);
+    status = f_string_dynamic_append_nulless(alias, &rule->alias);
 
     if (F_status_is_error(status)) {
       controller_error_print(main.data->error, F_status_set_fine(status), "f_string_dynamic_append_nulless", F_true, main.thread);
@@ -3045,8 +3141,8 @@ extern "C" {
     if (F_status_is_error(status)) {
       controller_rule_item_error_print(main.data->error, cache->action, for_item, main.thread);
 
-      rule->status = controller_status_simplify_error(F_status_set_fine(status));
-      return rule->status;
+      rule->status[0] = controller_status_simplify_error(F_status_set_fine(status));
+      return rule->status[0];
     }
 
     return F_none;
@@ -3078,6 +3174,7 @@ extern "C" {
     f_array_length_t i = 0;
     f_array_length_t j = 0;
     uint8_t type = 0;
+    uint8_t action = 0;
 
     // save the current name item and line number to restore on return.
     const f_array_length_t line_item = cache->action.line_item;
@@ -3154,12 +3251,12 @@ extern "C" {
       else if (fl_string_dynamic_compare_string(controller_string_name_s, cache->action.name_item, controller_string_name_length) == F_equal_to) {
         type = controller_rule_setting_type_name;
       }
-      else if (fl_string_dynamic_compare_string(controller_string_need_s, cache->action.name_item, controller_string_need_length) == F_equal_to) {
-        type = controller_rule_setting_type_need;
-      }
       else if (fl_string_dynamic_compare_string(controller_string_nice_s, cache->action.name_item, controller_string_nice_length) == F_equal_to) {
         type = controller_rule_setting_type_nice;
       }
+      else if (fl_string_dynamic_compare_string(controller_string_on_s, cache->action.name_item, controller_string_on_length) == F_equal_to) {
+        type = controller_rule_setting_type_on;
+      }
       else if (fl_string_dynamic_compare_string(controller_string_parameter_s, cache->action.name_item, controller_string_parameter_length) == F_equal_to) {
         type = controller_rule_setting_type_parameter;
       }
@@ -3175,12 +3272,6 @@ extern "C" {
       else if (fl_string_dynamic_compare_string(controller_string_user_s, cache->action.name_item, controller_string_user_length) == F_equal_to) {
         type = controller_rule_setting_type_user;
       }
-      else if (fl_string_dynamic_compare_string(controller_string_want_s, cache->action.name_item, controller_string_want_length) == F_equal_to) {
-        type = controller_rule_setting_type_want;
-      }
-      else if (fl_string_dynamic_compare_string(controller_string_wish_s, cache->action.name_item, controller_string_wish_length) == F_equal_to) {
-        type = controller_rule_setting_type_wish;
-      }
       else {
         if (main.data->warning.verbosity == f_console_verbosity_debug) {
           f_thread_mutex_lock(&main.thread->lock.print);
@@ -4697,13 +4788,14 @@ extern "C" {
         continue;
       }
 
-      if (cache->content_actions.array[i].used != 2) {
+      // The "on" Rule Setting.
+      if (cache->content_actions.array[i].used != 4) {
 
         if (main.data->error.verbosity != f_console_verbosity_quiet) {
           f_thread_mutex_lock(&main.thread->lock.print);
 
           fprintf(main.data->error.to.stream, "%c", f_string_eol_s[0]);
-          fprintf(main.data->error.to.stream, "%s%sRule setting requires exactly two Content.%s%c", main.data->error.context.before->string, main.data->error.prefix ? main.data->error.prefix : f_string_empty_s, main.data->error.context.after->string, f_string_eol_s[0]);
+          fprintf(main.data->error.to.stream, "%s%sRule setting requires exactly four Content.%s%c", main.data->error.context.before->string, main.data->error.prefix ? main.data->error.prefix : f_string_empty_s, main.data->error.context.after->string, f_string_eol_s[0]);
 
           // get the current line number within the settings item.
           cache->action.line_item = line_item;
@@ -4723,21 +4815,122 @@ extern "C" {
         continue;
       }
 
-      if (type == controller_rule_setting_type_need) {
-        setting_values = &rule->need;
+
+      if (fl_string_dynamic_partial_compare_string(controller_string_freeze_s, cache->buffer_item, controller_string_freeze_length, cache->content_actions.array[i].array[0]) == F_equal_to) {
+        action = controller_rule_action_type_freeze;
+      }
+      else if (fl_string_dynamic_partial_compare_string(controller_string_kill_s, cache->buffer_item, controller_string_kill_length, cache->content_actions.array[i].array[0]) == F_equal_to) {
+        action = controller_rule_action_type_kill;
       }
-      else if (type == controller_rule_setting_type_want) {
-        setting_values = &rule->want;
+      else if (fl_string_dynamic_partial_compare_string(controller_string_pause_s, cache->buffer_item, controller_string_pause_length, cache->content_actions.array[i].array[0]) == F_equal_to) {
+        action = controller_rule_action_type_pause;
       }
-      else if (type == controller_rule_setting_type_wish) {
-        setting_values = &rule->wish;
+      else if (fl_string_dynamic_partial_compare_string(controller_string_reload_s, cache->buffer_item, controller_string_reload_length, cache->content_actions.array[i].array[0]) == F_equal_to) {
+        action = controller_rule_action_type_reload;
       }
+      else if (fl_string_dynamic_partial_compare_string(controller_string_restart_s, cache->buffer_item, controller_string_restart_length, cache->content_actions.array[i].array[0]) == F_equal_to) {
+        action = controller_rule_action_type_restart;
+      }
+      else if (fl_string_dynamic_partial_compare_string(controller_string_resume_s, cache->buffer_item, controller_string_resume_length, cache->content_actions.array[i].array[0]) == F_equal_to) {
+        action = controller_rule_action_type_resume;
+      }
+      else if (fl_string_dynamic_partial_compare_string(controller_string_start_s, cache->buffer_item, controller_string_start_length, cache->content_actions.array[i].array[0]) == F_equal_to) {
+        action = controller_rule_action_type_start;
+      }
+      else if (fl_string_dynamic_partial_compare_string(controller_string_stop_s, cache->buffer_item, controller_string_stop_length, cache->content_actions.array[i].array[0]) == F_equal_to) {
+        action = controller_rule_action_type_stop;
+      }
+      else if (fl_string_dynamic_partial_compare_string(controller_string_thaw_s, cache->buffer_item, controller_string_thaw_length, cache->content_actions.array[i].array[0]) == F_equal_to) {
+        action = controller_rule_action_type_thaw;
+      }
+      else {
+        if (main.data->error.verbosity != f_console_verbosity_quiet) {
+          f_thread_mutex_lock(&main.thread->lock.print);
 
-      status = f_string_dynamics_increase_by(controller_default_allocation_step, setting_values);
+          fprintf(main.data->error.to.stream, "%c", f_string_eol_s[0]);
+          fprintf(main.data->error.to.stream, "%s%sRule setting's first value has '", main.data->error.context.before->string, main.data->error.prefix ? main.data->error.prefix : f_string_empty_s);
+          fprintf(main.data->error.to.stream, "%s%s", main.data->error.context.after->string, main.data->error.notable.before->string);
+          f_print_dynamic_partial(main.data->error.to.stream, cache->buffer_item, cache->content_actions.array[i].array[1]);
+          fprintf(main.data->error.to.stream, "%s", main.data->error.notable.after->string);
+          fprintf(main.data->error.to.stream, "%s' but only supports %s, %s, %s, %s, %s", main.data->error.context.before->string, controller_string_freeze_s, controller_string_kill_s, controller_string_pause_s, controller_string_reload_s, controller_string_restart_s);
+          fprintf(main.data->error.to.stream, "%s, %s, %s, and %s.%s%c", controller_string_resume_s, controller_string_start_s, controller_string_stop_s, controller_string_thaw_s, main.data->error.context.after->string, f_string_eol_s[0]);
+
+          // get the current line number within the settings item.
+          cache->action.line_item = line_item;
+          f_fss_count_lines(cache->buffer_item, cache->object_actions.array[i].start, &cache->action.line_item);
+
+          cache->action.line_action = ++cache->action.line_item;
+
+          controller_rule_error_print_cache(main.data->error, cache->action, F_false);
+
+          controller_print_unlock_flush(main.data->error.to.stream, &main.thread->lock.print);
+        }
+
+        if (F_status_is_error_not(status_return)) {
+          status_return = F_status_set_error(F_valid_not);
+        }
+
+        continue;
+      }
+
+      for (j = 0; j < rule->ons.used; ++j) {
+        if (rule->ons.array[j].action == action) break;
+      } // for
+
+      if (j == rule->ons.used) {
+        status = controller_rule_ons_increase(&rule->ons);
+      }
 
       if (F_status_is_error(status)) {
-        controller_rule_error_print(main.data->error, cache->action, F_status_set_fine(status), "f_string_dynamics_increase_by", F_true, F_false, main.thread);
+        controller_rule_error_print(main.data->error, cache->action, F_status_set_fine(status), "controller_rule_ons_increase", F_true, F_false, main.thread);
+      }
+      else {
+        if (fl_string_dynamic_partial_compare_string(controller_string_need_s, cache->buffer_item, controller_string_need_length, cache->content_actions.array[i].array[1]) == F_equal_to) {
+          setting_values = &rule->ons.array[j].need;
+        }
+        else if (fl_string_dynamic_partial_compare_string(controller_string_want_s, cache->buffer_item, controller_string_want_length, cache->content_actions.array[i].array[1]) == F_equal_to) {
+          setting_values = &rule->ons.array[j].want;
+        }
+        else if (fl_string_dynamic_partial_compare_string(controller_string_wish_s, cache->buffer_item, controller_string_wish_length, cache->content_actions.array[i].array[1]) == F_equal_to) {
+          setting_values = &rule->ons.array[j].wish;
+        }
+        else {
+          if (main.data->error.verbosity != f_console_verbosity_quiet) {
+            f_thread_mutex_lock(&main.thread->lock.print);
+
+            fprintf(main.data->error.to.stream, "%c", f_string_eol_s[0]);
+            fprintf(main.data->error.to.stream, "%s%sRule setting's second value has '", main.data->error.context.before->string, main.data->error.prefix ? main.data->error.prefix : f_string_empty_s);
+            fprintf(main.data->error.to.stream, "%s%s", main.data->error.context.after->string, main.data->error.notable.before->string);
+            f_print_dynamic_partial(main.data->error.to.stream, cache->buffer_item, cache->content_actions.array[i].array[1]);
+            fprintf(main.data->error.to.stream, "%s", main.data->error.notable.after->string);
+            fprintf(main.data->error.to.stream, "%s' but only supports %s, %s, and %s.%s%c", main.data->error.context.before->string, controller_string_need_s, controller_string_want_s, controller_string_wish_s, main.data->error.context.after->string, f_string_eol_s[0]);
+
+            // get the current line number within the settings item.
+            cache->action.line_item = line_item;
+            f_fss_count_lines(cache->buffer_item, cache->object_actions.array[i].start, &cache->action.line_item);
+
+            cache->action.line_action = ++cache->action.line_item;
+
+            controller_rule_error_print_cache(main.data->error, cache->action, F_false);
+
+            controller_print_unlock_flush(main.data->error.to.stream, &main.thread->lock.print);
+          }
+
+          if (F_status_is_error_not(status_return)) {
+            status_return = F_status_set_error(F_valid_not);
+          }
+
+          continue;
+        }
+
+        status = f_string_dynamics_increase_by(controller_default_allocation_step, setting_values);
+
+        if (F_status_is_error(status)) {
+          controller_rule_error_print(main.data->error, cache->action, F_status_set_fine(status), "f_string_dynamics_increase_by", F_true, F_false, main.thread);
+        }
+      }
 
+      if (F_status_is_error(status)) {
         if (F_status_set_fine(status) == F_memory_not) {
           status_return = status;
           break;
@@ -4758,7 +4951,7 @@ extern "C" {
         continue;
       }
 
-      status = controller_rule_id_construct(main, cache->buffer_item, cache->content_actions.array[i].array[0], cache->content_actions.array[i].array[1], &setting_values->array[setting_values->used]);
+      status = controller_rule_id_construct(main, cache->buffer_item, cache->content_actions.array[i].array[2], cache->content_actions.array[i].array[3], &setting_values->array[setting_values->used]);
 
       if (F_status_is_error(status)) {
         setting_values->array[setting_values->used].used = 0;
@@ -4810,9 +5003,9 @@ extern "C" {
           f_thread_mutex_lock(&main.thread->lock.print);
 
           fprintf(main.data->error.to.stream, "%c", f_string_eol_s[0]);
-          fprintf(main.data->error.to.stream, "%s%sThe rule item action second parameter '", main.data->error.context.before->string, main.data->error.prefix ? main.data->error.prefix : f_string_empty_s);
+          fprintf(main.data->error.to.stream, "%s%sThe rule item action third parameter '", main.data->error.context.before->string, main.data->error.prefix ? main.data->error.prefix : f_string_empty_s);
           fprintf(main.data->error.to.stream, "%s%s", main.data->error.context.after->string, main.data->error.notable.before->string);
-          f_print_dynamic_partial(main.data->error.to.stream, cache->buffer_item, cache->content_actions.array[i].array[1]);
+          f_print_dynamic_partial(main.data->error.to.stream, cache->buffer_item, cache->content_actions.array[i].array[2]);
           fprintf(main.data->error.to.stream, "%s", main.data->error.notable.after->string);
           fprintf(main.data->error.to.stream, "%s' must be a base path name, such as %llu '", main.data->error.context.before->string, cache->buffer_path.used);
           fprintf(main.data->error.to.stream, "%s%s", main.data->error.context.after->string, main.data->error.notable.before->string);
@@ -4828,7 +5021,13 @@ extern "C" {
         continue;
       }
 
-      setting_values->used++;
+      rule->ons.array[j].action = action;
+
+      ++setting_values->used;
+
+      if (j == rule->ons.used) {
+        ++rule->ons.used;
+      }
     } // for
 
     // resore the current name item and line number, which there should already be enough allocated space for.
@@ -5019,6 +5218,16 @@ extern "C" {
     } // for
 
     fprintf(data->output.stream, "  }%c", f_string_eol_s[0]);
+    fprintf(data->output.stream, "  %s%s%s {%c", data->context.set.important.before->string, controller_string_parameter_s, data->context.set.important.after->string, f_string_eol_s[0]);
+
+    for (i = 0; i < rule.parameter.used; ++i) {
+
+      if (rule.parameter.array[i].name.used && rule.parameter.array[i].value.used) {
+        fprintf(data->output.stream, "    %s %s=%s %s%c", rule.parameter.array[i].name.string, data->context.set.important.before->string, data->context.set.important.after->string, rule.parameter.array[i].value.string, f_string_eol_s[0]);
+      }
+    } // for
+
+    fprintf(data->output.stream, "  }%c", f_string_eol_s[0]);
     fprintf(data->output.stream, "  %s%s%s {%c", data->context.set.important.before->string, controller_string_group_s, data->context.set.important.after->string, f_string_eol_s[0]);
 
     if (rule.has & controller_rule_has_group) {
@@ -5037,44 +5246,77 @@ extern "C" {
     } // for
 
     fprintf(data->output.stream, "  }%c", f_string_eol_s[0]);
-    fprintf(data->output.stream, "  %s%s%s {%c", data->context.set.important.before->string, controller_string_need_s, data->context.set.important.after->string, f_string_eol_s[0]);
+    fprintf(data->output.stream, "  %s%s%s {%c", data->context.set.important.before->string, controller_string_on_s, data->context.set.important.after->string, f_string_eol_s[0]);
 
-    for (i = 0; i < rule.need.used; ++i) {
+    for (i = 0; i < rule.ons.used; ++i) {
 
-      if (rule.need.array[i].used) {
-        fprintf(data->output.stream, "    %s%c", rule.need.array[i].string, f_string_eol_s[0]);
+      fprintf(data->output.stream, "    %s%s%s {%c", data->context.set.important.before->string, controller_string_action_s, data->context.set.important.after->string, f_string_eol_s[0]);
+
+      {
+        f_string_t action = "";
+
+        if (rule.ons.array[i].action == controller_rule_action_type_freeze) {
+          action = controller_string_freeze_s;
+        }
+        else if (rule.ons.array[i].action == controller_rule_action_type_kill) {
+          action = controller_string_kill_s;
+        }
+        else if (rule.ons.array[i].action == controller_rule_action_type_pause) {
+          action = controller_string_pause_s;
+        }
+        else if (rule.ons.array[i].action == controller_rule_action_type_reload) {
+          action = controller_string_reload_s;
+        }
+        else if (rule.ons.array[i].action == controller_rule_action_type_restart) {
+          action = controller_string_restart_s;
+        }
+        else if (rule.ons.array[i].action == controller_rule_action_type_resume) {
+          action = controller_string_resume_s;
+        }
+        else if (rule.ons.array[i].action == controller_rule_action_type_start) {
+          action = controller_string_start_s;
+        }
+        else if (rule.ons.array[i].action == controller_rule_action_type_stop) {
+          action = controller_string_stop_s;
+        }
+        else if (rule.ons.array[i].action == controller_rule_action_type_thaw) {
+          action = controller_string_thaw_s;
+        }
+
+        fprintf(data->output.stream, "      %s%s%s %s%c", data->context.set.important.before->string, controller_string_type_s, data->context.set.important.after->string, action, f_string_eol_s[0]);
       }
-    } // for
 
-    fprintf(data->output.stream, "  }%c", f_string_eol_s[0]);
+      fprintf(data->output.stream, "      %s%s%s {%c", data->context.set.important.before->string, controller_string_need_s, data->context.set.important.after->string, f_string_eol_s[0]);
 
-    fprintf(data->output.stream, "  %s%s%s {%c", data->context.set.important.before->string, controller_string_parameter_s, data->context.set.important.after->string, f_string_eol_s[0]);
+      for (j = 0; j < rule.ons.array[i].need.used; ++j) {
 
-    for (i = 0; i < rule.parameter.used; ++i) {
+        if (rule.ons.array[i].need.array[j].used) {
+          fprintf(data->output.stream, "        %s%c", rule.ons.array[i].need.array[j].string, f_string_eol_s[0]);
+        }
+      } // for
 
-      if (rule.parameter.array[i].name.used && rule.parameter.array[i].value.used) {
-        fprintf(data->output.stream, "    %s %s=%s %s%c", rule.parameter.array[i].name.string, data->context.set.important.before->string, data->context.set.important.after->string, rule.parameter.array[i].value.string, f_string_eol_s[0]);
-      }
-    } // for
+      fprintf(data->output.stream, "      }%c", f_string_eol_s[0]);
+      fprintf(data->output.stream, "      %s%s%s {%c", data->context.set.important.before->string, controller_string_want_s, data->context.set.important.after->string, f_string_eol_s[0]);
 
-    fprintf(data->output.stream, "  }%c", f_string_eol_s[0]);
-    fprintf(data->output.stream, "  %s%s%s {%c", data->context.set.important.before->string, controller_string_want_s, data->context.set.important.after->string, f_string_eol_s[0]);
+      for (j = 0; j < rule.ons.array[i].want.used; ++j) {
 
-    for (i = 0; i < rule.want.used; ++i) {
+        if (rule.ons.array[i].want.array[j].used) {
+          fprintf(data->output.stream, "        %s%c", rule.ons.array[i].want.array[j].string, f_string_eol_s[0]);
+        }
+      } // for
 
-      if (rule.want.array[i].used) {
-        fprintf(data->output.stream, "    %s%c", rule.want.array[i].string, f_string_eol_s[0]);
-      }
-    } // for
+      fprintf(data->output.stream, "      }%c", f_string_eol_s[0]);
+      fprintf(data->output.stream, "      %s%s%s {%c", data->context.set.important.before->string, controller_string_wish_s, data->context.set.important.after->string, f_string_eol_s[0]);
 
-    fprintf(data->output.stream, "  }%c", f_string_eol_s[0]);
-    fprintf(data->output.stream, "  %s%s%s {%c", data->context.set.important.before->string, controller_string_wish_s, data->context.set.important.after->string, f_string_eol_s[0]);
+      for (j = 0; j < rule.ons.array[i].wish.used; ++j) {
 
-    for (i = 0; i < rule.wish.used; ++i) {
+        if (rule.ons.array[i].wish.array[j].used) {
+          fprintf(data->output.stream, "        %s%c", rule.ons.array[i].wish.array[j].string, f_string_eol_s[0]);
+        }
+      } // for
 
-      if (rule.wish.array[i].used) {
-        fprintf(data->output.stream, "    %s%c", rule.wish.array[i].string, f_string_eol_s[0]);
-      }
+      fprintf(data->output.stream, "      }%c", f_string_eol_s[0]);
+      fprintf(data->output.stream, "    }%c", f_string_eol_s[0]);
     } // for
 
     fprintf(data->output.stream, "  }%c", f_string_eol_s[0]);
@@ -5200,6 +5442,12 @@ extern "C" {
 
         if (status_lock == F_signal || F_status_is_error(status_lock)) break;
 
+        if (fl_string_dynamic_compare(caller->rule.alias, process->rule.alias) == F_equal_to) {
+          f_thread_unlock(&main.thread->lock.rule);
+
+          continue;
+        }
+
         skip = F_false;
 
         for (j = 0; j < caller->stack.used; ++j) {
@@ -5336,7 +5584,7 @@ extern "C" {
         }
 
         if (required && (process->options & controller_process_option_require)) {
-          if (F_status_is_error(process->rule.status)) {
+          if (controller_rule_status_is_error(process->action, process->rule)) {
             status = F_status_set_error(F_require);
 
             f_thread_unlock(&process->lock);
@@ -5351,7 +5599,7 @@ extern "C" {
 
             break;
           }
-          else if (process->rule.status == F_known_not) {
+          else if (controller_rule_status_is_available(process->action, process->rule)) {
             required_not_run = F_true;
           }
         }
@@ -5372,7 +5620,7 @@ extern "C" {
         continue;
       }
 
-      if (process->rule.status == F_known_not || (process->state == controller_process_state_active || process->state == controller_process_state_busy)) {
+      if (!controller_rule_status_is_error(process->action, process->rule) && (process->state == controller_process_state_active || process->state == controller_process_state_busy)) {
         f_thread_unlock(&process->lock);
 
         status = controller_process_wait(main, process);
@@ -5407,7 +5655,7 @@ extern "C" {
           if ((process->options & controller_process_option_require)) {
             f_thread_unlock(&process->lock);
 
-            if (F_status_is_error(process->rule.status)) {
+            if (controller_rule_status_is_error(process->action, process->rule)) {
               status = F_status_set_error(F_require);
 
               f_thread_unlock(&process->active);
index b1f2c82ed16ced42b6d9db5b00db2cf0d52793c1..8cff5de948f7d8d9c681eed65568d69b65182e5a 100644 (file)
@@ -500,6 +500,42 @@ extern "C" {
 #endif // _di_controller_rule_id_construct_
 
 /**
+ * Check to see if the given Rule has status F_known_not for the given Rule Action.
+ *
+ * The global Rule status is checked for error and any errors on the global Rule status will result in F_false.
+ *
+ * @param action
+ *   The Rule Action type.
+ * @param rule
+ *   The Rule.
+ *
+ * @return
+ *   F_true on available (status is F_known_not).
+ *   F_false on unavailable.
+ */
+#ifndef _di_controller_rule_status_is_available_
+  extern f_status_t controller_rule_status_is_available(const uint8_t action, const controller_rule_t rule) f_gcc_attribute_visibility_internal;
+#endif // _di_controller_rule_status_is_available_
+
+/**
+ * Check to see if the given Rule has status is designated as an error for the given Rule Action.
+ *
+ * The global Rule status is checked for error and any errors on the global Rule status will result in F_true.
+ *
+ * @param action
+ *   The Rule Action type.
+ * @param rule
+ *   The Rule.
+ *
+ * @return
+ *   F_true if status represents an error.
+ *   F_false if status does not represent an error.
+ */
+#ifndef _di_controller_rule_status_is_error_
+  extern f_status_t controller_rule_status_is_error(const uint8_t action, const controller_rule_t rule) f_gcc_attribute_visibility_internal;
+#endif // _di_controller_rule_status_is_error_
+
+/**
  * Read the content within the buffer, extracting all valid items after determining their type for some rule file.
  *
  * This will perform additional FSS read functions as appropriate.
@@ -696,7 +732,7 @@ extern "C" {
  * @param is_normal
  *   If TRUE, then this operates as an entry or control.
  *   If FALSE, then this operates as an exit.
- * @param rule_id
+ * @param alias
  *   The string identifying the rule.
  *   This is constructed from the path parts to the file without the file extension and without the settings directory prefix.
  *   "/etc/controller/rules/example/my.rule" would have a rule id of "example/my".
@@ -725,7 +761,7 @@ extern "C" {
  * @see fll_fss_basic_list_read().
  */
 #ifndef _di_controller_rule_read_
-  extern f_status_t controller_rule_read(const bool is_normal, const f_string_static_t rule_id, controller_main_t main, controller_cache_t *cache, controller_rule_t *rule) f_gcc_attribute_visibility_internal;
+  extern f_status_t controller_rule_read(const bool is_normal, const f_string_static_t alias, controller_main_t main, controller_cache_t *cache, controller_rule_t *rule) f_gcc_attribute_visibility_internal;
 #endif // _di_controller_rule_read_
 
 /**
index 0df6abd81a2676306ca7755deb2f2bf73f9a6963..1245486d36bb1dfeb22c90da72d0a39ce260dbbc 100644 (file)
@@ -6,7 +6,8 @@ main:
   consider serial s_3
   consider serial s_4
   consider serial s_5
+  consider serial s_6
 
-  stop serial s_6
+  stop serial s_1
 
   ready
index de7c6962cb4d071dd4e84efa869a31ce1c6f9ff0..62fbb40eb97df2d1f8dab15e4d6bb7a56f0d7d79 100644 (file)
@@ -4,7 +4,7 @@ setting:
   name "Sleep 1 Seconds."
   nice 15
   limit nice 1 2
-  need asynchronous sleep_10
+  on start need asynchronous sleep_10
 
 script:
   start echo "Sleeping 1: $(date -u), depends: 10"
index 40f3f4c206562893fc3a2b0298d639cdaee0f2d4..37aca16a9c8f58b58b07722e6bee663a7cf82d12 100644 (file)
@@ -4,7 +4,7 @@ setting:
   name "Sleep 2 Seconds."
   nice 15
   limit nice 1 2
-  need asynchronous sleep_10
+  on start need asynchronous sleep_10
 
 script:
   start echo "Sleeping 2: $(date -u), depends: 10"
index de403b6a4bfe603da4e6864f97db0114c27c09f5..f946fd218f39a9b7bcec158689d0fbd95a263e85 100644 (file)
@@ -4,7 +4,7 @@ setting:
   name "Sleep 3 Seconds."
   nice 15
   limit nice 1 2
-  need asynchronous sleep_8
+  on start need asynchronous sleep_8
 
 script:
   start echo "Sleeping 3: $(date -u), depends: 8"
index cb55d79c540f5c526af57d6ab149140f5acf0a82..82760c90539f429d73c4ab8ecbffaff2ecdffd43 100644 (file)
@@ -15,7 +15,7 @@ setting:
 
   affinity 0
 
-  need script require_me
+  on start need script require_me
 
 script:
   start {
index 560bd7867db264d812200ed699ab2cbfc4fde98b..ec101efa4e4bf6fa898d552e84915029ff81a17b 100644 (file)
@@ -2,7 +2,7 @@
 
 setting:
   name "Script #2"
-  need script succeed
+  on start need script succeed
 
 script:
   start {
@@ -14,4 +14,3 @@ script:
 
     my_function
   }
-
index 5c45c2e3e8311097019aae90aff6c2ece64267ec..e4568b0adc947f0b18380bd5a76a4d500bad5dc1 100644 (file)
@@ -2,6 +2,7 @@
 
 setting:
   name "Serial 1"
+  on stop need serial s_2
 
 script:
   start {
index 29c044e7f1a4f26cfb6ac795f73affc40d09db32..2bb7cec37a301e5f8581583b33adbda675dee948 100644 (file)
@@ -2,7 +2,8 @@
 
 setting:
   name "Serial 2"
-  need serial s_1
+  on start need serial s_1
+  on stop need serial s_3
 
 script:
   start {
index 3960d115ae58c12e3f08202bc3dd28670bff4953..cd1d84e6f930b70827f5e2d7281385a7f580dd2b 100644 (file)
@@ -2,7 +2,8 @@
 
 setting:
   name "Serial 3"
-  need serial s_2
+  on start need serial s_2
+  on stop need serial s_4
 
 script:
   start {
index 34f4ee9b977f88dd5faff5be8378698d1e8c2bd6..6be76e0b726f3361a556a01d07bdc433705fb9f1 100644 (file)
@@ -2,7 +2,8 @@
 
 setting:
   name "Serial 4"
-  need serial s_3
+  on start need serial s_3
+  on stop need serial s_5
 
 script:
   start {
index 5dde94d6826d2d3018eeb4e94b654cf500960df2..a08bd1f49e86dd774d5fc18dcbd54fe05c8b00fd 100644 (file)
@@ -2,7 +2,8 @@
 
 setting:
   name "Serial 5"
-  need serial s_4
+  on start need serial s_4
+  on stop need serial s_6
 
 script:
   start {
index fda3365c0d2a18e8a69fe91793ebcd904546590c..23be70d99a78653f74aac2636f58d88165b6675e 100644 (file)
@@ -2,7 +2,7 @@
 
 setting:
   name "Serial 6"
-  need serial s_5
+  on start need serial s_5
 
 script:
   start {
index a0bdc56493626786911b1a7f757187ede915bf0f..f165c5d4e6eddba5801742d2de6a4af843b43de9 100644 (file)
@@ -6,4 +6,4 @@
 setting:
   name "Setup System Devices"
 
-  need boot modules
+  on start need boot modules
index 2536c7fe908610d2b7a102e5b9b4b9f82bd26bc8..58b85697e09a453cab3274b6fb23a61c19a9c718 100644 (file)
@@ -6,7 +6,7 @@
 setting:
   name "Setup Kernel Modules"
 
-  need boot filesystem
+  on start need boot filesystem
 
 script:
   start {
index d5c1c110f04444e48920293525afe5a89dc8fc78..aff02b4da28ccdf025fc76c910ccfc7f4706d45b 100644 (file)
@@ -6,7 +6,7 @@
 setting:
   name "Loopback Device"
 
-  need boot modules
+  on start need boot modules
 
 script:
   start {
index 66c3f0392f29324a30595f88d7a115160b71467c..660070ea7310634043ad801f8d478b6d5ee3aa85 100644 (file)
@@ -87,7 +87,7 @@ Entry Documentation:
 
 Entry Rule Documentation:
   There are multiple Entry Actions that are considered "rule" Actions.
-  These are: "freeze", "kill", "pause", "reload", "restart", "resume", "start", and "stop".
+  These are: "freeze", "kill", "pause", "reload", "restart", "resume", "start", "stop", and "thaw".
 
   The "rule" Actions immediately execute a named rule file.
   The first Action Parameter represents the rule directory, which is a relative directory path the rule file is to be found.
index d04ecf8e7011ee7e59d104924dcad5798847a53e..84f0719c4789edffc0686570bf63f60b7a5ddd54 100644 (file)
@@ -82,7 +82,7 @@ Exit Documentation:
 
 Entry Rule Documentation:
   There are multiple Entry Actions that are considered "rule" Actions.
-  These are: "freeze", "kill", "pause", "reload", "restart", "resume", "start", and "stop".
+  These are: "freeze", "kill", "pause", "reload", "restart", "resume", "start", "stop", and "thaw".
 
   The "rule" Actions immediately execute a named rule file.
   The first Action Parameter represents the rule directory, which is a relative directory path the rule file is to be found.
index a0e0013b6f73ff4e65a359e236de6b1a229efd88..d52db9447eb4004c7a6574501fb128735dcddefd 100644 (file)
@@ -17,44 +17,61 @@ Rule Documentation:
     "group": A set of group names or IDs to execute as with the first group being the primary group and all remaining being supplementary groups.
     "limit": Define a resource limit to use (multiple limits may be specified, but only once for each type).
     "name": A name used to represent this rule, which is printed to the user, screen, logs, etc...
-    "need": A single rule required to be executed (must exist and must succeed) before this rule starts.
     "nice": A single niceness value to run all processes executed within this rule as (-20 gets to be greediest in CPU usage and 19 being the nicest in CPU usage).
+    "on": Define a Rule Action in which a specified dependency is needed, wanted, or wished for.
     "parameter": A statically defined IKI name and its associated value for use in this rule file. @todo make sure this is implemented and make sure to perform iki substitutions (with "define" being a reserved iki parameter name for environment variable usage, such as define:"PATH").
     "path": A single Content used to set a custom PATH environment variable value.
     "script": An executable name of a script, such as "bash", to use for the "script" Rule Type (which likely defaults to "bash" if not specified).
     "scheduler": A valid name of a scheduler to use followed by an optional priority number.
     "user": A single user name or ID to execute as.
-    "want": A single rule desired to be executed (may exist and must succeed) before this rule starts.
-    "wish": A single rule desired to be executed (may exist and is not required to succeed) before this rule starts.
 
-  In the case of "capability", if the user the controller program is run as does not have the desired capabilities already, they cannot be added.
-  This essentially maintains or reduces the capabilities already available.
-  Due to capabilities only being a draft in the POSIX standard, one may expect "capabilities" support may not be available and in such a case this setting will do nothing.
-  If the dependent project (f_capability) does not have libcap support enabled, then capabilities will be unsupported by the compilation of this project.
-
-  In the case of "control", the first argument is either "existing" or "new", where for "existing" the process is run inside the existing control used by the parent and when "new" the process is executed within a new control group namespace entirely.
-
-  In the case of "group" and "user", only users and groups that the user the controller program is being run as may be used.
-
-  In the case of "limit", first parameter must be one of the following: as, core, cpu, data, fsize, locks, memlock, msgqueue, nice, nofile, nproc, rss, rtprio, rttime, sigpending, and stack.
-  The second parameter repesents the soft limit.
-  The third parameter represents the hard limit.
-  This may be specified multiply times, but only once for each type.
-
-  In the case of "want" and "wish", if the desired rule is either not found or is otherwise disabled, then this will not fail or otherwise block the wanting or wishing rule.
-
-  In the case of "path", when specified, the PATH environment variable is automatically added to the "environment" setting.
-
-  In the case of "parameter", IKI variables are expanded in a pre-process manner and will be removed prior to any execution.
-  Any IKI variables referencing an undefined parameter will be fully removed.
-  These parameters are only exposed in the specific rule file in which they are defined and cannot be shared between rules.
-  A "parameter" variable and an "environment" variable are mutually exclusive but an environment variable, in theory, can have an IKI variable assigned to it inside of a "script".
-  These IKI variables are only substituted within a Rule Item's Content (and not within a Rule Setting nor within a Rule Item's Object).
-
-  In the case of "scheduler", the valid range of the priority number is dependent on the scheduler.
-  For example, non-real-time schedulers (such as "idle") only support a value of 0 whereas real-time schedulers (such as "fifo") only support an inclusive range of 1 to 99.
-  Supported non-real-time schedulers are: "batch", "idle", and "other" (aka: normal/default).
-  Supported real-time schedulers are: "deadline", "fifo", "round_robin".
+  In the case of "capability"\:
+    If the user the controller program is run as does not have the desired capabilities already, they cannot be added.
+    This essentially maintains or reduces the capabilities already available.
+    Due to capabilities only being a draft in the POSIX standard, one may expect "capabilities" support may not be available and in such a case this setting will do nothing.
+    If the dependent project (f_capability) does not have libcap support enabled, then capabilities will be unsupported by the compilation of this project.
+
+  In the case of "control"\:
+    The first argument is either "existing" or "new", where for "existing" the process is run inside the existing control used by the parent and when "new" the process is executed within a new control group namespace entirely.
+
+  In the case of "group" and "user"\:
+    Only users and groups that the user the controller program is being run as may be used.
+
+  In the case of "limit"\:
+    The first parameter must be one of: "as", "core", "cpu", "data", "fsize", "locks", "memlock", "msgqueue", "nice", "nofile", "nproc", "rss", "rtprio", "rttime", "sigpending", or "stack".
+    The second parameter repesents the soft limit.
+    The third parameter represents the hard limit.
+    This may be specified multiply times, but only once for each type.
+
+  In the case of "on"\:
+    The first parameter represents the Action the dependency exists under and must be one of: "freeze", "kill", "pause", "reload", "restart", "resume", "start", "stop", or "thaw".
+    The second parameter represents how the dependency is required and must be one of: "need", "want", or "wish".
+    The third parameter is a partial path to the rule file.
+    The fourth parameter represents the name of the rule file.
+
+    In the case of the second parameter\:
+      A "need" designates that the dependent rule is required to be executed (must exist and must succeed).
+      A "want" designates that the dependent rule is to be executed (may exist and if it does, then it must succeed).
+      A "wish" designates that the dependent rule is to be executed (may exist and if it does, but it does not need to succeed).
+
+      In the case of "want" and "wish", if the desired rule is either not found or is otherwise disabled, then this will not fail or otherwise block the wanting or wishing rule.
+
+  In the case of "path"\:
+    When specified, the PATH environment variable is automatically added to the "environment" setting.
+
+  In the case of "parameter"\:
+    IKI variables are expanded in a pre-process manner and will be removed prior to any execution.
+    Any IKI variables referencing an undefined parameter will be fully removed.
+    These parameters are only exposed in the specific rule file in which they are defined and cannot be shared between rules.
+    A "parameter" variable and an "environment" variable are mutually exclusive but an environment variable, in theory, can have an IKI variable assigned to it inside of a "script".
+    These IKI variables are only substituted within a Rule Item's Content (and not within a Rule Setting nor within a Rule Item's Object).
+    Note: IKI variables are not yet implemented.
+
+  In the case of "scheduler"\:
+    The valid range of the priority number is dependent on the scheduler.
+    For example, non-real-time schedulers (such as "idle") only support a value of 0 whereas real-time schedulers (such as "fifo") only support an inclusive range of 1 to 99.
+    Supported non-real-time schedulers are: "batch", "idle", and "other" (aka: normal/default).
+    Supported real-time schedulers are: "deadline", "fifo", "round_robin".
 
   There are four available Rule Types to choose from: "command", "service", "script", and "utility".
 
@@ -72,25 +89,16 @@ Rule Documentation:
 
   The "utility" Rule Type provides a "script" accompanied with a PID file (Process Identifier file).
 
-  There are nine primary inner Content Objects to perform: "freeze", "kill", "pause", "restart", "resume", "reload", "start", "stop", "thaw".
-
-  The "freeze" Object's Content is performed whenever this rule is executed using the freeze Action.
-  The "kill" Object's Content is performed whenever this rule is executed using the kill Action (which is, in general, a forced stop).
-  The "pause" Object's Content is performed whenever this rule is executed using the pause Action.
-  The "restart" Object's Content is performed whenever this rule is executed using the restart Action.
-  The "resume" Object's Content is performed whenever this rule is executed using the resume Action.
-  The "reload" Object's Content is performed whenever this rule is executed using the reload Action.
-  The "start" Object's Content is performed whenever this rule is executed using the start Action.
-  The "stop" Object's Content is performed whenever this rule is executed using the stop Action.
-  The "thaw" Object's Content is performed whenever this rule is executed using the thaw Action.
+  There are nine Rule Actions used to execute ("freeze", "kill", "pause", "restart", "resume", "reload", "start", "stop", and "thaw")\:
+    When "restart" Object's Content is not provided, then "start" and "stop" is called when the rule is executed using the restart Action, if both "start" and "stop" are provided.
+    When "reload", "start", or "stop" Object's Content are not provided, then no respective Action is performed.
 
-  When "restart" Object's Content is not provided, then "start" and "stop" is called when the rule is executed using the restart Action, if both "start" and "stop" are provided.
-  When "reload", "start", or "stop" Object's Content are not provided, then no respective Action is performed.
-  Commands are conditionally available depending on the presence of these, such as if "stop" is not provided then "stop" (and "restart") will not be available for the "control" program(s) to use.
+    Commands are conditionally available depending on the presence of these, such as if "stop" is not provided then "stop" (and "restart") will not be available for the "control" program(s) to use.
 
-  The "pid_file" Object's Content designates the path to the PID file created by the called program.
+  Thee are additional Rule Actions not used to execute ("pid_file" and "with")\:
+    The "pid_file" Object's Content designates the path to the PID file created by the called program.
 
-  The "with" Object's Content designates special flags designating very specific behavior to be applied to any single Rule Type.
-  The following flags are supported:
-    "full_path": Used only by Rule Types that execute something, wherein the entire full path is used for execution and is assigned as argument[0] (such as "/bin/bash").
-                 When not specified, the path provided is used and the argument[0] will be the base name (such as "bash").
+    The "with" Object's Content designates special flags designating very specific behavior to be applied to any single Rule Type.
+    The following flags are supported:
+      "full_path": Used only by Rule Types that execute something, wherein the entire full path is used for execution and is assigned as argument[0] (such as "/bin/bash").
+                   When not specified, the path provided is used and the argument[0] will be the base name (such as "bash").
index cf670bfd66eb92bcf4d76d4a60df6180328d90ea..d04df8a57f943faecf7021d2302b9b136d992052 100644 (file)
@@ -4,16 +4,20 @@ Time Documentation:
   The Controller program utilizes the unit of measurement called a "Time", represented with uppercase "T".
   For comparison, a unit of Time is equivalent to a nanosecond, or 10^-9 seconds.
   A MegaTime (MT) is therefore equivalent to a millisecond such that a millisecond is 10^-3 seconds.
+
   A unit of Time is intended to represent some unit of Time such that a single 64-bit integer may hold all units of Time for a single calendar year.
   This unit of Time does not and must not include Years (unlike Unixtime).
   To convert from Time to Unixtime, one must have a year (which could be assumed to be the current year) and then calculate all of those calendar oddities.
+
   A unit of Time by default is assumed to be in UTC.
   1 (Earth) year ~= 31536000000000000 Time or 31536000 GT (GigaTime).
   1 (Earth) day = 86400000000000 Time or 86400 GT (GigaTime).
   1 (Earth) hour = 3600000000000 Time or 3600 GT (GigaTime).
   1 (Earth) minute = 60000000000 Time or 60 GT (GigaTime).
   1 (Earth) second = 1000000000 Time or 1 GT (GigaTime).
+
   Consequentially, 1 day in units of Time is easily represented as 86.4 TT (TeraTime).
+
   The Time may be stored in its "year string format".
   In this format, a Year may be prepended to the Time followed by a single colon ':' to associate a year with the Time.
   This Year has no minimum or maximum but may not have decimals.
index 375a0d7096c033182c5f30fe26b7b4846bdb4793..e3a824553ca8f310975ba5037a76bad8db12317e 100644 (file)
@@ -37,15 +37,13 @@ Rule Specification:
     "group": One or more Content representing group names or group ids.
     "limit": Three Content, with the first representing a valid resource type and the second and third being a valid resource limit number (positive whole number or 0).
     "name": One Content, must have at least 1 graph character (non-whitespace printing character) (leading and trailing whitespace are trimmed off).
-    "need": Two Content, the first being a partial path and the second being a rule file name without extension (such as "boot" "modules").
     "nice": One Content, must be a valid number for process "niceness" (Any whole number inclusively between -20 to 19).
+    "on": Four Content, the first being a Rule Action, the second being "need", "want", or "wish", the third being a partial path, and the fourth being a Rule file name without ".rule" extension.
     "parameter": Two Content, the first Content must be a case-sensitive valid IKI name and the second being an IKI value.
     "path": One Content representing a valid PATH environment string (such as "/bin:/sbin:/usr/bin").
     "scheduler": One or Two Content representing a scheduler name and the optional numeric priority (Any whole number inclusively between 0 and 99).
     "script": One Content representing a valid program name or path (such as "bash" or "/bin/bash").
     "user": One Content representing a user name or user id.
-    "want": Two Content, the first being a partial path and the second being a rule file name without extension (such as "boot" "modules").
-    "wish": Two Content, the first being a partial path and the second being a rule file name without extension (such as "boot" "modules").
 
   The "command" and "script" Rule Types allow the following the FSS-0001 (Extended)\:
     "freeze": One or more Content representing a program being executed and its arguments.