From b3886b81006d75c436b50073ad6481590334c303 Mon Sep 17 00:00:00 2001 From: Kevin Day Date: Thu, 22 Apr 2021 20:49:21 -0500 Subject: [PATCH] Progress: redesign processing logic to accommodate different process Rule Actions and update dependency design accordingly. 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. --- level_3/controller/c/private-common.c | 79 +++- level_3/controller/c/private-common.h | 253 +++++++++-- level_3/controller/c/private-controller.c | 183 ++++---- level_3/controller/c/private-controller.h | 83 +++- level_3/controller/c/private-rule.c | 464 ++++++++++++++++----- level_3/controller/c/private-rule.h | 40 +- .../data/settings/example/exits/serial.exit | 3 +- .../example/rules/asynchronous/sleep_1.rule | 2 +- .../example/rules/asynchronous/sleep_2.rule | 2 +- .../example/rules/asynchronous/sleep_3.rule | 2 +- .../settings/example/rules/command/multiple.rule | 2 +- .../data/settings/example/rules/script/fail.rule | 3 +- .../data/settings/example/rules/serial/s_1.rule | 1 + .../data/settings/example/rules/serial/s_2.rule | 3 +- .../data/settings/example/rules/serial/s_3.rule | 3 +- .../data/settings/example/rules/serial/s_4.rule | 3 +- .../data/settings/example/rules/serial/s_5.rule | 3 +- .../data/settings/example/rules/serial/s_6.rule | 2 +- .../data/settings/rules/boot/devices.rule | 2 +- .../data/settings/rules/boot/modules.rule | 2 +- .../data/settings/rules/net/loopback.rule | 2 +- level_3/controller/documents/entry.txt | 2 +- level_3/controller/documents/exit.txt | 2 +- level_3/controller/documents/rule.txt | 108 ++--- level_3/controller/documents/time.txt | 4 + level_3/controller/specifications/rule.txt | 4 +- 26 files changed, 957 insertions(+), 300 deletions(-) diff --git a/level_3/controller/c/private-common.c b/level_3/controller/c/private-common.c index 959d22a..36db682 100644 --- a/level_3/controller/c/private-common.c +++ b/level_3/controller/c/private-common.c @@ -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) { diff --git a/level_3/controller/c/private-common.h b/level_3/controller/c/private-common.h index 2b7bb51..a6656a3 100644 --- a/level_3/controller/c/private-common.h +++ b/level_3/controller/c/private-common.h @@ -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 diff --git a/level_3/controller/c/private-controller.c b/level_3/controller/c/private-controller.c index 6cfad59..7569fdc 100644 --- a/level_3/controller/c/private-controller.c +++ b/level_3/controller/c/private-controller.c @@ -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) { diff --git a/level_3/controller/c/private-controller.h b/level_3/controller/c/private-controller.h index 5d13d64..b15f320 100644 --- a/level_3/controller/c/private-controller.h +++ b/level_3/controller/c/private-controller.h @@ -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 diff --git a/level_3/controller/c/private-rule.c b/level_3/controller/c/private-rule.c index b1ee47b..4f03c60 100644 --- a/level_3/controller/c/private-rule.c +++ b/level_3/controller/c/private-rule.c @@ -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); diff --git a/level_3/controller/c/private-rule.h b/level_3/controller/c/private-rule.h index b1f2c82..8cff5de 100644 --- a/level_3/controller/c/private-rule.h +++ b/level_3/controller/c/private-rule.h @@ -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_ /** diff --git a/level_3/controller/data/settings/example/exits/serial.exit b/level_3/controller/data/settings/example/exits/serial.exit index 0df6abd..1245486 100644 --- a/level_3/controller/data/settings/example/exits/serial.exit +++ b/level_3/controller/data/settings/example/exits/serial.exit @@ -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 diff --git a/level_3/controller/data/settings/example/rules/asynchronous/sleep_1.rule b/level_3/controller/data/settings/example/rules/asynchronous/sleep_1.rule index de7c696..62fbb40 100644 --- a/level_3/controller/data/settings/example/rules/asynchronous/sleep_1.rule +++ b/level_3/controller/data/settings/example/rules/asynchronous/sleep_1.rule @@ -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" diff --git a/level_3/controller/data/settings/example/rules/asynchronous/sleep_2.rule b/level_3/controller/data/settings/example/rules/asynchronous/sleep_2.rule index 40f3f4c..37aca16 100644 --- a/level_3/controller/data/settings/example/rules/asynchronous/sleep_2.rule +++ b/level_3/controller/data/settings/example/rules/asynchronous/sleep_2.rule @@ -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" diff --git a/level_3/controller/data/settings/example/rules/asynchronous/sleep_3.rule b/level_3/controller/data/settings/example/rules/asynchronous/sleep_3.rule index de403b6..f946fd2 100644 --- a/level_3/controller/data/settings/example/rules/asynchronous/sleep_3.rule +++ b/level_3/controller/data/settings/example/rules/asynchronous/sleep_3.rule @@ -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" diff --git a/level_3/controller/data/settings/example/rules/command/multiple.rule b/level_3/controller/data/settings/example/rules/command/multiple.rule index cb55d79..82760c9 100644 --- a/level_3/controller/data/settings/example/rules/command/multiple.rule +++ b/level_3/controller/data/settings/example/rules/command/multiple.rule @@ -15,7 +15,7 @@ setting: affinity 0 - need script require_me + on start need script require_me script: start { diff --git a/level_3/controller/data/settings/example/rules/script/fail.rule b/level_3/controller/data/settings/example/rules/script/fail.rule index 560bd78..ec101ef 100644 --- a/level_3/controller/data/settings/example/rules/script/fail.rule +++ b/level_3/controller/data/settings/example/rules/script/fail.rule @@ -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 } - diff --git a/level_3/controller/data/settings/example/rules/serial/s_1.rule b/level_3/controller/data/settings/example/rules/serial/s_1.rule index 5c45c2e..e4568b0 100644 --- a/level_3/controller/data/settings/example/rules/serial/s_1.rule +++ b/level_3/controller/data/settings/example/rules/serial/s_1.rule @@ -2,6 +2,7 @@ setting: name "Serial 1" + on stop need serial s_2 script: start { diff --git a/level_3/controller/data/settings/example/rules/serial/s_2.rule b/level_3/controller/data/settings/example/rules/serial/s_2.rule index 29c044e..2bb7cec 100644 --- a/level_3/controller/data/settings/example/rules/serial/s_2.rule +++ b/level_3/controller/data/settings/example/rules/serial/s_2.rule @@ -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 { diff --git a/level_3/controller/data/settings/example/rules/serial/s_3.rule b/level_3/controller/data/settings/example/rules/serial/s_3.rule index 3960d11..cd1d84e 100644 --- a/level_3/controller/data/settings/example/rules/serial/s_3.rule +++ b/level_3/controller/data/settings/example/rules/serial/s_3.rule @@ -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 { diff --git a/level_3/controller/data/settings/example/rules/serial/s_4.rule b/level_3/controller/data/settings/example/rules/serial/s_4.rule index 34f4ee9..6be76e0 100644 --- a/level_3/controller/data/settings/example/rules/serial/s_4.rule +++ b/level_3/controller/data/settings/example/rules/serial/s_4.rule @@ -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 { diff --git a/level_3/controller/data/settings/example/rules/serial/s_5.rule b/level_3/controller/data/settings/example/rules/serial/s_5.rule index 5dde94d..a08bd1f 100644 --- a/level_3/controller/data/settings/example/rules/serial/s_5.rule +++ b/level_3/controller/data/settings/example/rules/serial/s_5.rule @@ -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 { diff --git a/level_3/controller/data/settings/example/rules/serial/s_6.rule b/level_3/controller/data/settings/example/rules/serial/s_6.rule index fda3365..23be70d 100644 --- a/level_3/controller/data/settings/example/rules/serial/s_6.rule +++ b/level_3/controller/data/settings/example/rules/serial/s_6.rule @@ -2,7 +2,7 @@ setting: name "Serial 6" - need serial s_5 + on start need serial s_5 script: start { diff --git a/level_3/controller/data/settings/rules/boot/devices.rule b/level_3/controller/data/settings/rules/boot/devices.rule index a0bdc56..f165c5d 100644 --- a/level_3/controller/data/settings/rules/boot/devices.rule +++ b/level_3/controller/data/settings/rules/boot/devices.rule @@ -6,4 +6,4 @@ setting: name "Setup System Devices" - need boot modules + on start need boot modules diff --git a/level_3/controller/data/settings/rules/boot/modules.rule b/level_3/controller/data/settings/rules/boot/modules.rule index 2536c7f..58b8569 100644 --- a/level_3/controller/data/settings/rules/boot/modules.rule +++ b/level_3/controller/data/settings/rules/boot/modules.rule @@ -6,7 +6,7 @@ setting: name "Setup Kernel Modules" - need boot filesystem + on start need boot filesystem script: start { diff --git a/level_3/controller/data/settings/rules/net/loopback.rule b/level_3/controller/data/settings/rules/net/loopback.rule index d5c1c11..aff02b4 100644 --- a/level_3/controller/data/settings/rules/net/loopback.rule +++ b/level_3/controller/data/settings/rules/net/loopback.rule @@ -6,7 +6,7 @@ setting: name "Loopback Device" - need boot modules + on start need boot modules script: start { diff --git a/level_3/controller/documents/entry.txt b/level_3/controller/documents/entry.txt index 66c3f03..660070e 100644 --- a/level_3/controller/documents/entry.txt +++ b/level_3/controller/documents/entry.txt @@ -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. diff --git a/level_3/controller/documents/exit.txt b/level_3/controller/documents/exit.txt index d04ecf8..84f0719 100644 --- a/level_3/controller/documents/exit.txt +++ b/level_3/controller/documents/exit.txt @@ -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. diff --git a/level_3/controller/documents/rule.txt b/level_3/controller/documents/rule.txt index a0e0013..d52db94 100644 --- a/level_3/controller/documents/rule.txt +++ b/level_3/controller/documents/rule.txt @@ -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"). diff --git a/level_3/controller/documents/time.txt b/level_3/controller/documents/time.txt index cf670bf..d04df8a 100644 --- a/level_3/controller/documents/time.txt +++ b/level_3/controller/documents/time.txt @@ -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. diff --git a/level_3/controller/specifications/rule.txt b/level_3/controller/specifications/rule.txt index 375a0d7..e3a8245 100644 --- a/level_3/controller/specifications/rule.txt +++ b/level_3/controller/specifications/rule.txt @@ -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. -- 1.8.3.1