From 692bffdf39f3f4515d25c00fa2564a43aace2185 Mon Sep 17 00:00:00 2001 From: Kevin Day Date: Fri, 27 Nov 2020 20:41:28 -0600 Subject: [PATCH] Progress: controller program. --- level_3/controller/c/controller.c | 5 +- level_3/controller/c/controller.h | 12 +- level_3/controller/c/private-controller.c | 71 ++++ level_3/controller/c/private-controller.h | 150 +++++--- level_3/controller/c/private-entry.c | 528 +++++++++++++++++++++++++--- level_3/controller/c/private-entry.h | 126 +++++-- level_3/controller/c/private-rule.c | 150 ++++---- level_3/controller/c/private-rule.h | 27 +- level_3/controller/documents/entry.txt | 16 +- level_3/controller/specifications/entry.txt | 5 +- 10 files changed, 859 insertions(+), 231 deletions(-) diff --git a/level_3/controller/c/controller.c b/level_3/controller/c/controller.c index 147127e..550a32b 100644 --- a/level_3/controller/c/controller.c +++ b/level_3/controller/c/controller.c @@ -26,7 +26,8 @@ extern "C" { fprintf(output.stream, "%c", f_string_eol[0]); fll_program_print_help_option(output, context, controller_short_interruptable, controller_long_interruptable, f_console_symbol_short_enable, f_console_symbol_long_enable, " Designate that this program can be interrupted."); - fll_program_print_help_option(output, context, controller_short_settings, controller_long_settings, f_console_symbol_short_enable, f_console_symbol_long_enable, " Specify a custom settings path."); + fll_program_print_help_option(output, context, controller_short_pid, controller_long_pid, f_console_symbol_short_enable, f_console_symbol_long_enable, " Specify a custom pid file path, such as '" controller_path_pid "'."); + fll_program_print_help_option(output, context, controller_short_settings, controller_long_settings, f_console_symbol_short_enable, f_console_symbol_long_enable, " Specify a custom settings path, such as '" controller_path_settings "'."); fll_program_print_help_option(output, context, controller_short_test, controller_long_test, f_console_symbol_short_enable, f_console_symbol_long_enable, " Run in test mode, where nothing is actually run (a simulation)."); fll_program_print_help_usage(output, context, controller_name, ""); @@ -39,6 +40,8 @@ extern "C" { f_return_status controller_main(const f_console_arguments_t arguments, controller_data_t *data) { f_status_t status = F_none; + // @todo somewhere in here, check to see if the standard pid file exists before attempting to start (when in normal operation mode). + { const f_console_parameters_t parameters = f_macro_console_parameters_t_initialize(data->parameters, controller_total_parameters); diff --git a/level_3/controller/c/controller.h b/level_3/controller/c/controller.h index 0bcbbb0..2ceb047 100644 --- a/level_3/controller/c/controller.h +++ b/level_3/controller/c/controller.h @@ -64,15 +64,23 @@ extern "C" { #endif // _di_controller_name_ #ifndef _di_controller_defines_ + + // must be at least 2. + #define controller_default_allocation_step 4 + + #define controller_path_pid "/var/run/controller.pid" #define controller_path_settings "/etc/controller" + #define controller_path_pid_length 23 #define controller_path_settings_length 15 #define controller_short_interruptable "i" + #define controller_short_pid "p" #define controller_short_settings "s" #define controller_short_test "t" #define controller_long_interruptable "interruptable" + #define controller_long_pid "pid" #define controller_long_settings "settings" #define controller_long_test "test" @@ -88,6 +96,7 @@ extern "C" { controller_parameter_version, controller_parameter_interruptable, + controller_parameter_pid, controller_parameter_settings, controller_parameter_test, }; @@ -104,11 +113,12 @@ extern "C" { f_console_parameter_t_initialize(f_console_standard_short_debug, f_console_standard_long_debug, 0, 0, f_console_type_inverse), \ f_console_parameter_t_initialize(f_console_standard_short_version, f_console_standard_long_version, 0, 0, f_console_type_inverse), \ f_console_parameter_t_initialize(controller_short_interruptable, controller_long_interruptable, 0, 0, f_console_type_normal), \ + f_console_parameter_t_initialize(controller_short_pid, controller_long_pid, 0, 1, f_console_type_normal), \ f_console_parameter_t_initialize(controller_short_settings, controller_long_settings, 0, 1, f_console_type_normal), \ f_console_parameter_t_initialize(controller_short_test, controller_long_test, 0, 0, f_console_type_normal), \ } - #define controller_total_parameters 12 + #define controller_total_parameters 13 #endif // _di_controller_defines_ #ifndef _di_controller_data_t_ diff --git a/level_3/controller/c/private-controller.c b/level_3/controller/c/private-controller.c index cc67912..2e5c73a 100644 --- a/level_3/controller/c/private-controller.c +++ b/level_3/controller/c/private-controller.c @@ -8,6 +8,77 @@ extern "C" { #endif +#ifndef _di_controller_file_load_ + f_return_status controller_file_load(const controller_data_t data, const controller_setting_t setting, const f_string_t path_prefix, const f_string_static_t path_name, const f_string_t path_suffix, const f_string_length_t path_prefix_length, const f_string_length_t path_suffix_length, f_string_dynamic_t *path_file, f_string_dynamic_t *buffer) { + f_status_t status = F_none; + f_file_t file = f_file_t_initialize; + + status = fl_string_append(path_prefix, path_prefix_length, path_file); + + if (F_status_is_error_not(status)) { + status = fl_string_append(f_path_separator, f_path_separator_length, path_file); + } + + if (F_status_is_error(status)) { + fll_error_print(data.error, F_status_set_fine(status), "fl_string_append", F_true); + return status; + } + + status = fl_string_dynamic_append(path_name, path_file); + + if (F_status_is_error(status)) { + fll_error_print(data.error, F_status_set_fine(status), "fl_string_dynamic_append", F_true); + return status; + } + + status = fl_string_append(f_path_extension_separator, f_path_extension_separator_length, path_file); + + if (F_status_is_error_not(status)) { + status = fl_string_append(path_suffix, path_suffix_length, path_file); + } + + if (F_status_is_error(status)) { + fll_error_print(data.error, F_status_set_fine(status), "fl_string_append", F_true); + return status; + } + + status = fl_string_dynamic_terminate_after(path_file); + + if (F_status_is_error(status)) { + fll_error_print(data.error, F_status_set_fine(status), "fl_string_dynamic_terminate_after", F_true); + return status; + } + + const f_string_length_t path_length = setting.path_setting.used + f_path_separator_length + path_file->used; + char path[path_length + 1]; + + memcpy(path, setting.path_setting.string, setting.path_setting.used); + memcpy(path + setting.path_setting.used + f_path_separator_length, path_file->string, path_file->used); + + path[setting.path_setting.used] = f_path_separator[0]; + path[path_length] = 0; + + status = f_file_stream_open(path, 0, &file); + + if (F_status_is_error(status)) { + fll_error_file_print(data.error, F_status_set_fine(status), "f_file_stream_open", F_true, path_file->string, "open", fll_error_file_type_file); + } + else { + status = f_file_stream_read(file, 1, buffer); + + if (F_status_is_error(status)) { + fll_error_file_print(data.error, F_status_set_fine(status), "f_file_stream_read", F_true, path_file->string, "read", fll_error_file_type_file); + } + } + + f_file_stream_close(F_true, &file); + + if (F_status_is_error(status)) return status; + + return F_none; + } +#endif // _di_controller_file_load_ + #ifndef _di_controller_validate_define_name_ f_return_status controller_validate_environment_name(const f_string_static_t name) { diff --git a/level_3/controller/c/private-controller.h b/level_3/controller/c/private-controller.h index 245a56e..5bc8902 100644 --- a/level_3/controller/c/private-controller.h +++ b/level_3/controller/c/private-controller.h @@ -23,11 +23,13 @@ extern "C" { #define controller_string_environment "environment" #define controller_string_failsafe "failsafe" #define controller_string_group "group" + #define controller_string_item "item" #define controller_string_main "main" #define controller_string_name "name" #define controller_string_need "need" #define controller_string_path "path" #define controller_string_pid "pid" + #define controller_string_ready "ready" #define controller_string_restart "restart" #define controller_string_reload "reload" #define controller_string_rule "rule" @@ -53,11 +55,13 @@ extern "C" { #define controller_string_environment_length 11 #define controller_string_failsafe_length 8 #define controller_string_group_length 5 + #define controller_string_item_length 4 #define controller_string_main_length 4 #define controller_string_name_length 4 #define controller_string_need_length 4 #define controller_string_path_length 4 #define controller_string_pid_length 3 + #define controller_string_ready_length 5 #define controller_string_restart_length 7 #define controller_string_reload_length 6 #define controller_string_rule_length 4 @@ -292,13 +296,14 @@ extern "C" { } #endif // _di_controller_rules_t_ -#ifndef _di_controller_entry_item_t_ +#ifndef _di_controller_entry_action_t_ enum { - controller_entry_item_type_consider = 1, - controller_entry_item_type_failsafe, - controller_entry_item_type_group, - controller_entry_item_type_rule, - controller_entry_item_type_timeout, + controller_entry_action_type_consider = 1, + controller_entry_action_type_failsafe, + controller_entry_action_type_item, + controller_entry_action_type_ready, + controller_entry_action_type_rule, + controller_entry_action_type_timeout, }; #define controller_entry_rule_code_asynchronous 0x1 @@ -316,121 +321,180 @@ extern "C" { f_string_length_t line; f_number_unsigned_t timeout; - f_string_dynamic_t id; - } controller_entry_item_t; + f_status_t status; - #define controller_entry_item_t_initialize \ + f_string_dynamics_t parameters; + } controller_entry_action_t; + + #define controller_entry_action_t_initialize \ { \ 0, \ 0, \ 0, \ 0, \ - f_string_dynamic_t_initialize, \ + F_unknown, \ + f_string_dynamics_t_initialize, \ } - #define macro_controller_entry_item_t_delete_simple(item) \ - f_macro_string_dynamics_t_delete_simple(item.id) -#endif // _di_controller_entry_item_t_ + #define macro_controller_entry_action_t_delete_simple(item) \ + f_macro_string_dynamics_t_delete_simple(item.parameters) +#endif // _di_controller_entry_action_t_ -#ifndef _di_controller_entry_items_t_ +#ifndef _di_controller_entry_actions_t_ typedef struct { - controller_entry_item_t *array; + controller_entry_action_t *array; f_array_length_t size; f_array_length_t used; - } controller_entry_items_t; + } controller_entry_actions_t; - #define controller_entry_items_t_initialize \ + #define controller_entry_actions_t_initialize \ { \ 0, \ 0, \ 0, \ } - #define macro_controller_entry_items_t_delete_simple(items) \ + #define macro_controller_entry_actions_t_delete_simple(items) \ items.used = items.size; \ while (items.used > 0) { \ items.used--; \ macro_controller_rule_t_delete_simple(items.array[items.used]); \ if (!items.used) { \ - if (f_memory_delete((void **) & items.array, sizeof(controller_entry_item_t), items.size)) { \ + if (f_memory_delete((void **) & items.array, sizeof(controller_entry_action_t), items.size)) { \ items.size = 0; \ } \ } \ } -#endif // _di_controller_entry_items_t_ +#endif // _di_controller_entry_actions_t_ -#ifndef _di_controller_entry_list_t_ +#ifndef _di_controller_entry_item_t_ typedef struct { f_string_length_t line; + f_string_dynamic_t name; - controller_entry_items_t items; - } controller_entry_list_t; + controller_entry_actions_t actions; + } controller_entry_item_t; - #define controller_entry_list_t_initialize \ + #define controller_entry_item_t_initialize \ { \ 0, \ f_string_dynamic_t_initialize, \ - controller_entry_items_t_initialize, \ + controller_entry_actions_t_initialize, \ } - #define macro_controller_entry_list_t_delete_simple(list) \ + #define macro_controller_entry_item_t_delete_simple(list) \ f_macro_string_dynamic_t_delete_simple(list.name) \ - macro_controller_entry_items_t_delete_simple(list.items) -#endif // _di_controller_entry_list_t_ + macro_controller_entry_actions_t_delete_simple(list.actions) +#endif // _di_controller_entry_item_t_ -#ifndef _di_controller_entry_lists_t_ +#ifndef _di_controller_entry_items_t_ typedef struct { - controller_entry_list_t *array; + controller_entry_item_t *array; f_array_length_t size; f_array_length_t used; - } controller_entry_lists_t; + } controller_entry_items_t; - #define controller_entry_lists_t_initialize \ + #define controller_entry_items_t_initialize \ { \ 0, \ 0, \ 0, \ } - #define macro_controller_entry_lists_t_delete_simple(lists) \ - lists.used = lists.size; \ - while (lists.used > 0) { \ - lists.used--; \ - macro_controller_rule_t_delete_simple(lists.array[lists.used]); \ - if (!lists.used) { \ - if (f_memory_delete((void **) & lists.array, sizeof(controller_entry_list_t), lists.size)) { \ - lists.size = 0; \ + #define macro_controller_entry_items_t_delete_simple(items) \ + items.used = items.size; \ + while (items.used > 0) { \ + items.used--; \ + macro_controller_rule_t_delete_simple(items.array[items.used]); \ + if (!items.used) { \ + if (f_memory_delete((void **) & items.array, sizeof(controller_entry_item_t), items.size)) { \ + items.size = 0; \ } \ } \ } -#endif // _di_controller_entry_lists_t_ +#endif // _di_controller_entry_items_t_ + +#ifndef _di_controller_entry_t_ + typedef struct { + f_status_t status; + controller_entry_items_t items; + } controller_entry_t; + + #define controller_entry_t_initialize \ + { \ + F_unknown, \ + controller_entry_items_t_initialize, \ + } + + #define macro_controller_entry_t_delete_simple(entry) \ + macro_controller_entry_items_t_delete_simple(entry.items) +#endif // _di_controller_entry_t_ #ifndef _di_controller_setting_t typedef struct { bool interruptable; + bool ready; f_string_dynamic_t path_setting; - controller_entry_lists_t entry_lists; + controller_entry_t entry; controller_rules_t rules; } controller_setting_t; #define controller_setting_t_initialize \ { \ F_false, \ + F_false, \ f_string_dynamic_t_initialize, \ + controller_entry_t_initialize, \ controller_rules_t_initialize, \ } #define macro_controller_setting_t_delete_simple(setting) \ f_macro_string_dynamic_t_delete_simple(setting.path_setting) \ - macro_controller_entry_lists_t_delete_simple(entry_lists) \ + macro_controller_entry_items_t_delete_simple(entry_lists) \ + macro_controller_entry_t_delete_simple(setting.entry) \ f_macro_string_dynamic_t_delete_simple(setting.rules) #endif // _di_controller_setting_t /** + * Load a file from the controller settings directory. + * + * @param data + * The program data. + * @param setting + * The controller settings data. + * @param path_prefix + * The path prefix, such as 'entries' from '/etc/controller/entries/default.entry'. + * @param path_name + * The path name, such as 'default' from '/etc/controller/entries/default.entry'. + * @param path_suffix + * The path suffix, such as 'entry' from '/etc/controller/entries/default.entry'. + * @param path_prefix_length + * The length of the prefix path. + * @param path_suffix_length + * The length of the suffix path. + * @param path_file + * This is updated with a partial path to the given file. + * @param buffer + * The buffer to load the file into. + * + * @return + * F_none on success. + * + * Errors (with error bit) from: f_file_stream_open(). + * Errors (with error bit) from: f_file_stream_read(). + * + * @see f_file_stream_open() + * @see f_file_stream_read() + */ +#ifndef _di_controller_file_load_ + extern f_return_status controller_file_load(const controller_data_t data, const controller_setting_t setting, const f_string_t path_prefix, const f_string_static_t path_name, const f_string_t path_suffix, const f_string_length_t path_prefix_length, const f_string_length_t path_suffix_length, f_string_dynamic_t *path_file, f_string_dynamic_t *buffer) f_gcc_attribute_visibility_internal; +#endif // _di_controller_file_load_ + +/** * Validate that the given string is a valid environment variable name. * * A valid environment variable name must begin with an alpha-character or an underscore. diff --git a/level_3/controller/c/private-entry.c b/level_3/controller/c/private-entry.c index b93169c..b5331ad 100644 --- a/level_3/controller/c/private-entry.c +++ b/level_3/controller/c/private-entry.c @@ -5,113 +5,539 @@ extern "C" { #endif -#ifndef _di_controller_entry_lists_increase_by_ - f_return_status controller_entry_lists_increase_by(const f_array_length_t amount, controller_entry_lists_t *lists) { +#ifndef _di_controller_entry_actions_increase_by_ + f_return_status controller_entry_actions_increase_by(const f_array_length_t amount, controller_entry_actions_t *actions) { f_status_t status = F_none; - f_string_length_t size = lists->size + amount; + f_string_length_t size = actions->size + amount; if (size > f_array_length_t_size) { - if (lists->size == f_array_length_t_size) { + if (actions->size == f_array_length_t_size) { return F_status_set_error(F_array_too_large); } - size = lists->size; + size = actions->size; status = F_array_too_large; } - const f_status_t status_resize = f_memory_resize((void **) & lists->array, sizeof(controller_entry_lists_t), lists->size, size); + const f_status_t status_resize = f_memory_resize((void **) & actions->array, sizeof(controller_entry_action_t), actions->size, size); if (F_status_is_error(status_resize)) return status_resize; - lists->size = size; + actions->size = size; return status; } -#endif // _di_controller_entry_lists_increase_by_ +#endif // _di_controller_entry_actions_increase_by_ -#ifndef _di_controller_entry_list_read_ - f_return_status controller_entry_list_read(const controller_data_t data, const controller_setting_t setting, const f_string_static_t entry_name, controller_entry_cache_t *cache, controller_entry_lists_t *lists) { +#ifndef _di_controller_entry_actions_read_ + f_return_status controller_entry_actions_read(const controller_data_t data, const controller_setting_t setting, const f_string_range_t content_range, controller_entry_cache_t *cache, controller_entry_actions_t *actions) { f_status_t status = F_none; + f_status_t status_action = F_none; + + actions->used = 0; + cache->object_actions.used = 0; + cache->content_actions.used = 0; { - f_file_t file = f_file_t_initialize; + f_string_range_t range = content_range; + + status = fll_fss_extended_read(cache->buffer_file, &range, &cache->object_actions, &cache->content_actions, 0, 0, &cache->delimits, 0); + } + + if (F_status_is_error(status)) { + fll_error_print(data.error, F_status_set_fine(status), "fll_fss_extended_read", F_true); + + return status; + } + + status = fl_fss_apply_delimit(cache->delimits, &cache->buffer_file); + + if (F_status_is_error(status)) { + fll_error_print(data.error, F_status_set_fine(status), "fl_fss_apply_delimit", F_true); + + return status; + } + + cache->delimits.used = 0; + + status = controller_entry_actions_increase_by(cache->object_actions.used, actions); + + if (F_status_is_error(status)) { + fll_error_print(data.error, F_status_set_fine(status), "controller_entry_actions_increase_by", F_true); + + return status; + } - const f_string_length_t file_path_length = setting.path_setting.used + f_path_separator_length + controller_string_rules_length + f_path_separator_length + entry_name.used + f_path_separator_length; - char file_path[file_path_length + 1]; + controller_entry_action_t *action = 0; - memcpy(file_path, setting.path_setting.string, setting.path_setting.used); - memcpy(file_path + setting.path_setting.used + f_path_separator_length, controller_string_rules, controller_string_rules_length); - memcpy(file_path + setting.path_setting.used + f_path_separator_length + controller_string_rules_length + f_path_separator_length, entry_name.string, entry_name.used); + f_array_length_t allocate = 0; + f_array_length_t at_least = 0; + f_array_length_t at_most = 0; - file_path[setting.path_setting.used] = f_path_separator[0]; - file_path[setting.path_setting.used + f_path_separator_length + controller_string_rules_length] = f_path_separator[0]; - file_path[file_path_length - 1] = f_path_separator[0]; - file_path[file_path_length] = 0; + f_array_length_t i = 0; + f_array_length_t j = 0; - status = f_file_stream_open(file_path, 0, &file); + for (; i < cache->object_actions.used; ++i) { + + cache->line_action = 0; + cache->name_action.used = 0; + + action = &actions->array[actions->used]; + action->type = 0; + action->code = 0; + action->line = cache->line_action; + action->timeout = 0; + action->status = F_unknown; + action->parameters.used = 0; + + status = f_fss_count_lines(cache->buffer_file, cache->object_actions.array[i].start, &cache->line_action); + + if (F_status_is_error(status)) { + fll_error_print(data.error, F_status_set_fine(status), "f_fss_count_lines", F_true); + break; + } + + status = fl_string_dynamic_rip_nulless(cache->buffer_item, cache->object_actions.array[i], &cache->name_action); + + if (F_status_is_error(status)) { + fll_error_print(data.error, F_status_set_fine(status), "fl_string_dynamic_rip_nulless", F_true); + break; + } + + status = fl_string_dynamic_terminate_after(&cache->name_action); if (F_status_is_error(status)) { - fll_error_file_print(data.error, F_status_set_fine(status), "f_file_stream_open", F_true, entry_name.string, "open", fll_error_file_type_file); + fll_error_print(data.error, F_status_set_fine(status), "fl_string_dynamic_terminate_after", F_true); + break; + } + + if (fl_string_dynamic_compare_string(controller_string_consider, cache->name_action, controller_string_consider_length) == F_equal_to) { + actions->array[actions->used].type = controller_entry_action_type_consider; + } + else if (fl_string_dynamic_compare_string(controller_string_failsafe, cache->name_action, controller_string_failsafe_length) == F_equal_to) { + actions->array[actions->used].type = controller_entry_action_type_failsafe; + } + else if (fl_string_dynamic_compare_string(controller_string_item, cache->name_action, controller_string_item_length) == F_equal_to) { + actions->array[actions->used].type = controller_entry_action_type_item; + } + else if (fl_string_dynamic_compare_string(controller_string_ready, cache->name_action, controller_string_ready_length) == F_equal_to) { + actions->array[actions->used].type = controller_entry_action_type_ready; + } + else if (fl_string_dynamic_compare_string(controller_string_rule, cache->name_action, controller_string_rule_length) == F_equal_to) { + actions->array[actions->used].type = controller_entry_action_type_rule; + } + else if (fl_string_dynamic_compare_string(controller_string_timeout, cache->name_action, controller_string_timeout_length) == F_equal_to) { + actions->array[actions->used].type = controller_entry_action_type_timeout; } else { - status = f_file_stream_read(file, 1, &cache->buffer_entry); + if (data.warning.verbosity == f_console_verbosity_debug) { + fprintf(data.warning.to.stream, "%c", f_string_eol[0]); + fprintf(data.warning.to.stream, "%s%sUnknown entry item type.%s%c", data.warning.context.before->string, data.warning.prefix ? data.warning.prefix : "", data.warning.context.after->string, f_string_eol[0]); - if (F_status_is_error(status)) { - fll_error_file_print(data.error, F_status_set_fine(status), "f_file_stream_read", F_true, entry_name.string, "read", fll_error_file_type_file); + controller_entry_error_print(data.warning, *cache); } + + continue; + } + + if (action->type == controller_entry_action_type_consider || action->type == controller_entry_action_type_rule) { + allocate = cache->content_actions.array[i].used; + at_least = 1; + at_most = allocate; + } + else if (action->type == controller_entry_action_type_failsafe || action->type == controller_entry_action_type_item) { + allocate = 1; + at_least = 1; + at_most = 1; + } + else if (action->type == controller_entry_action_type_timeout) { + allocate = 2; + at_least = 2; + at_most = 2; + } + else { + // for: controller_entry_action_type_ready. + allocate = 0; + at_least = 0; + at_most = 0; } - f_file_stream_close(F_true, &file); + if (cache->content_actions.array[i].used < at_least || cache->content_actions.array[i].used > at_most) { + action->status = F_status_set_error(F_parameter); - if (F_status_is_error(status)) return status; - } + if (data.error.verbosity != f_console_verbosity_quiet) { + fprintf(data.error.to.stream, "%c", f_string_eol[0]); + fprintf(data.error.to.stream, "%s%sThe entry item action '", data.error.context.before->string, data.error.prefix ? data.error.prefix : ""); + fprintf(data.error.to.stream, "%s%s%s%s", data.error.context.after->string, data.error.notable.before->string, cache->name_action.string, data.error.notable.after->string); + fprintf(data.error.to.stream, "%s' requires ", data.error.context.before->string); - if (cache->buffer_entry.used) { - f_string_range_t range = f_macro_string_range_t_initialize(cache->buffer_entry.used); + if (action->type == controller_entry_action_type_failsafe || action->type == controller_entry_action_type_item) { + fprintf(data.error.to.stream, "%s%s%llu%s", data.error.context.after->string, data.error.notable.before->string, cache->line_action, data.error.notable.after->string); + fprintf(data.error.to.stream, "%s or more parameters.%s%c", data.error.context.before->string, data.error.context.after->string, f_string_eol[0]); + } + else { + uint8_t parameters = 0; - status = fll_fss_basic_list_read(cache->buffer_entry, &range, &cache->objects_list, &cache->contents_list, &cache->delimits, 0, &cache->comments); + if (action->type == controller_entry_action_type_failsafe || action->type == controller_entry_action_type_item) { + parameters = 1; + } + else if (action->type == controller_entry_action_type_timeout) { + parameters = 2; + } - if (F_status_is_error(status)) { - fll_error_print(data.error, F_status_set_fine(status), "fll_fss_basic_list_read", F_true); + fprintf(data.error.to.stream, "exactly ", data.error.context.before->string); + fprintf(data.error.to.stream, "%s%s%u%s", data.error.context.after->string, parameters, data.error.notable.after->string); + fprintf(data.error.to.stream, "%s parameters.%s%c", data.error.context.before->string, data.error.context.after->string, f_string_eol[0]); + } + } } else { - status = fl_fss_apply_delimit(cache->delimits, &cache->buffer_entry); + action->status = F_none; + } + + if (F_status_is_error(action->status)) { + if (F_status_is_error_not(status_action)) { + status_action = action->status; + } + + continue; + } + + if (allocate) { + status = fl_string_dynamics_increase_by(allocate, &action->parameters); if (F_status_is_error(status)) { - fll_error_print(data.error, F_status_set_fine(status), "fl_fss_apply_delimit", F_true); + fll_error_print(data.error, F_status_set_fine(status), "fl_string_dynamics_increase_by", F_true); + + if (F_status_is_error_not(status_action)) { + status_action = status; + } + + break; } + for (j = 0; j < allocate; ++j) { + + action->parameters.array[j].used = 0; + + status = fl_string_dynamic_partial_append_nulless(cache->buffer_file, cache->content_actions.array[i].array[j], &action->parameters.array[j]); + + if (F_status_is_error(status)) { + fll_error_print(data.error, F_status_set_fine(status), "fl_string_dynamic_partial_append_nulless", F_true); + + if (F_status_is_error_not(status_action)) { + status_action = status; + } + + break; + } + + action->parameters.used++; + } // for + + if (F_status_is_error(status)) break; + + // @todo validate if "timeout" paramter #1 is one of start, stop, or kill and parameter #2 is valid number. + // @todo validate if "consider" or "rule" parameters are valid (valid file path), (valid file basename), (then one of asynchronous, require, and/or wait). + // @todo: after entries are processed, check to see if "item" or "failsafe" reference existent items. } - cache->delimits.used = 0; - cache->comments.used = 0; + actions->used++; + } // for + + if (F_status_is_error(status_action)) { + return status_action; } - else { - // @todo: error out that there is no list, because a valid entry is required. - return F_status_set_error(F_data_not); + + return status; + } +#endif // _di_controller_entry_actions_read_ + +#ifndef _di_controller_entry_error_print_ + void controller_entry_error_print(const fll_error_print_t output, const controller_entry_cache_t cache) { + + if (output.verbosity != f_console_verbosity_quiet) { + fprintf(output.to.stream, "%s%sWhile processing ", output.context.before->string, output.prefix ? output.prefix : ""); + + if (cache.name_action.used) { + fprintf(output.to.stream, "action '"); + fprintf(output.to.stream, "%s%s%s%s", output.context.after->string, output.notable.before->string, cache.name_action.string, output.notable.after->string); + fprintf(output.to.stream, "%s' on line ", output.context.before->string); + fprintf(output.to.stream, "%s%s%llu%s", output.context.after->string, output.notable.before->string, cache.line_action, output.notable.after->string); + fprintf(output.to.stream, "%s for ", output.context.before->string); + } + + if (cache.name_item.used) { + fprintf(output.to.stream, "entry item '"); + fprintf(output.to.stream, "%s%s%s%s", output.context.after->string, output.notable.before->string, cache.name_item.string, output.notable.after->string); + fprintf(output.to.stream, "%s' on line ", output.context.before->string); + fprintf(output.to.stream, "%s%s%llu%s", output.context.after->string, output.notable.before->string, cache.line_list, output.notable.after->string); + fprintf(output.to.stream, "%s for ", output.context.before->string); + } + + fprintf(output.to.stream, "file '"); + fprintf(output.to.stream, "%s%s%s%s", output.context.after->string, output.notable.before->string, cache.name_file.string, output.notable.after->string); + fprintf(output.to.stream, "%s'.%s%c", output.context.before->string, output.context.after->string, f_string_eol[0]); } + } +#endif // _di_controller_entry_error_print_ - if (F_status_is_error_not(status) && cache->objects_items.used) { - status = controller_entry_lists_increase_by(cache->objects_list.used, lists); +#ifndef _di_controller_entry_items_increase_by_ + f_return_status controller_entry_items_increase_by(const f_array_length_t amount, controller_entry_items_t *items) { + f_status_t status = F_none; + f_string_length_t size = items->size + amount; + + if (size > f_array_length_t_size) { + if (items->size == f_array_length_t_size) { + return F_status_set_error(F_array_too_large); + } + + size = items->size; + status = F_array_too_large; + } + + const f_status_t status_resize = f_memory_resize((void **) & items->array, sizeof(controller_entry_items_t), items->size, size); + if (F_status_is_error(status_resize)) return status_resize; + + items->size = size; + return status; + } +#endif // _di_controller_entry_items_increase_by_ + +#ifndef _di_controller_entry_read_ + f_return_status controller_entry_read(const controller_data_t data, const controller_setting_t setting, const f_string_static_t entry_name, controller_entry_cache_t *cache, controller_entry_t *entry) { + f_status_t status = F_none; + + entry->status = F_unknown; + entry->items.used = 0; + + cache->line_action = 0; + cache->line_list = 0; + + cache->range_list.start = 1; + cache->range_list.stop = 0; + + cache->comments.used = 0; + cache->delimits.used = 0; + + cache->content_action.used = 0; + cache->content_actions.used = 0; + cache->content_items.used = 0; + + cache->object_actions.used = 0; + cache->object_items.used = 0; + + cache->buffer_file.used = 0; + cache->buffer_item.used = 0; + + cache->name_file.used = 0; + cache->name_action.used = 0; + cache->name_item.used = 0; + + status = controller_file_load(data, setting, controller_string_rules, entry_name, controller_string_rule, controller_string_rules_length, controller_string_rule_length, &cache->buffer_file, &cache->name_file); + + if (F_status_is_error_not(status)) { + if (cache->buffer_file.used) { + f_string_range_t range = f_macro_string_range_t_initialize(cache->buffer_file.used); + + status = fll_fss_basic_list_read(cache->buffer_file, &range, &cache->object_items, &cache->content_items, &cache->delimits, 0, &cache->comments); + + if (F_status_is_error(status)) { + fll_error_print(data.error, F_status_set_fine(status), "fll_fss_basic_list_read", F_true); + } + else { + status = fl_fss_apply_delimit(cache->delimits, &cache->buffer_file); + + if (F_status_is_error(status)) { + fll_error_print(data.error, F_status_set_fine(status), "fl_fss_apply_delimit", F_true); + } + } + } + else { + if (data.error.verbosity != f_console_verbosity_quiet) { + fprintf(data.error.to.stream, "%c", f_string_eol[0]); + fprintf(data.error.to.stream, "%s%sThe entry file is empty.%s%c", data.error.context.before->string, data.error.prefix ? data.error.prefix : "", data.error.context.after->string, f_string_eol[0]); + } + + status = F_status_set_error(F_data_not); + } + } + + if (F_status_is_error_not(status) && cache->object_actions.used) { + status = controller_entry_items_increase_by(cache->object_items.used, &entry->items); if (F_status_is_error(status)) { - fll_error_print(data.error, F_status_set_fine(status), "controller_entry_lists_increase_by", F_true); + fll_error_print(data.error, F_status_set_fine(status), "controller_entry_items_increase_by", F_true); } else { - for (f_array_length_t i = 0; i < cache->objects_list.used; ++i) { - // @todo - // @todo reserve index 0 for "main", checking each list if a "main" exists then assigning it to index 0). - // @todo be sure to provide an error if "main" does not exist at all, this is a critical failure. + // 0x1 = main found, 0x2 = found existing. + uint8_t code = 0; + + controller_entry_item_t *item = 0; + f_string_range_t *range = 0; + f_string_length_t j = 0; + + for (f_array_length_t i = 0; i < cache->object_items.used; ++i) { + + if (code & 0x2) { + code -= 0x2; + } + + range = 0; + + cache->line_action = 0; + cache->line_list = 0; + + cache->range_list = cache->object_items.array[i]; + + cache->comments.used = 0; + cache->delimits.used = 0; + + cache->content_action.used = 0; + cache->content_actions.used = 0; + + cache->object_actions.used = 0; + + cache->buffer_file.used = 0; + cache->buffer_item.used = 0; + + cache->name_action.used = 0; + cache->name_item.used = 0; + + status = controller_entry_items_increase_by(controller_default_allocation_step, &entry->items); + + if (F_status_is_error(status)) { + fll_error_print(data.error, F_status_set_fine(status), "controller_entry_items_increase_by", F_true); + break; + } + + status = fl_string_dynamic_partial_append_nulless(cache->buffer_file, cache->object_items.array[i], &cache->name_item); + + if (F_status_is_error(status)) { + fll_error_print(data.error, F_status_set_fine(status), "fl_string_dynamic_partial_append_nulless", F_true); + break; + } + + status = fl_string_dynamic_terminate(&cache->name_item); + + if (F_status_is_error(status)) { + fll_error_print(data.error, F_status_set_fine(status), "fl_string_dynamic_terminate", F_true); + break; + } + + status = f_fss_count_lines(cache->buffer_file, cache->object_items.array[i].start, &cache->line_list); + + if (F_status_is_error(status)) { + fll_error_print(data.error, F_status_set_fine(status), "f_fss_count_lines", F_true); + break; + } + + for (j = code & 0x1 ? 1 : 0; j < entry->items.used; ++j) { + + if (fl_string_dynamic_compare(entry->items.array[j].name, cache->name_item) == F_equal_to) { + if (data.warning.verbosity != f_console_verbosity_quiet) { + fprintf(data.warning.to.stream, "%c", f_string_eol[0]); + fprintf(data.warning.to.stream, "%s%sIgnoring duplicate entry item '", data.warning.context.before->string, data.warning.prefix ? data.warning.prefix : ""); + fprintf(data.warning.to.stream, "%s%s%s%s", data.warning.context.after->string, data.warning.notable.before->string, cache->name_file.string, data.warning.notable.after->string); + fprintf(data.warning.to.stream, "%s'.%s%c", data.warning.context.before->string, data.warning.context.after->string, f_string_eol[0]); + + controller_entry_error_print(data.warning, *cache); + } + + code |= 0x2; + break; + } + } // for + + if (code & 0x2) continue; + + range = &cache->content_items.array[i].array[0]; + + if (fl_string_dynamic_compare_string(controller_string_main, cache->name_item, controller_string_main_length) == F_equal_to) { + code |= 0x1; + + item = &entry->items.array[0]; + + if (!entry->items.used) { + entry->items.used = 1; + } + } + else if (entry->items.used) { + item = &entry->items.array[entry->items.used++]; + } + else { + + // skip position 0, which is reserved for "main". + item = &entry->items.array[1]; + + entry->items.used = 2; + } + + item->line = cache->line_list; + + status = fl_string_dynamic_append(cache->name_item, &item->name); + + if (F_status_is_error(status)) { + fll_error_print(data.error, F_status_set_fine(status), "fl_string_dynamic_append", F_true); + break; + } + + status = fl_string_dynamic_terminate(&item->name); + + if (F_status_is_error(status)) { + fll_error_print(data.error, F_status_set_fine(status), "fl_string_dynamic_terminate", F_true); + break; + } + + status = controller_entry_actions_read(data, setting, *range, cache, &item->actions); + + if (F_status_is_error(status)) { + controller_entry_error_print(data.error, *cache); + + if (F_status_set_fine(status) == F_memory_not || F_status_set_fine(status) == F_memory_allocation || F_status_set_fine(status) == F_memory_reallocation) { + break; + } + } } // for + + if (F_status_is_error_not(status) && code & 0x1) { + if (data.error.verbosity != f_console_verbosity_quiet) { + fprintf(data.error.to.stream, "%c", f_string_eol[0]); + fprintf(data.error.to.stream, "The required list '"); + fprintf(data.error.to.stream, "%s%s%s%s", data.error.context.after->string, data.error.notable.before->string, controller_string_main, data.error.notable.after->string); + fprintf(data.error.to.stream, "%s' was not found.%s%c", data.error.context.before->string, data.error.context.after->string, f_string_eol[0]); + } + + status = F_status_set_error(F_found_not); + } } } if (F_status_is_error(status)) { - //@todo someting like: controller_rule_error_print(data.error, *cache, for_item); + status = F_status_set_fine(status); - return status; + controller_entry_error_print(data.error, *cache); + + if (status == F_memory_not || status == F_memory_allocation || status == F_memory_reallocation) { + entry->status = F_memory; + } + else if (status == F_file_open_max || status == F_space_not || status == F_busy) { + entry->status = F_resource; + } + else if (status == F_access_denied || status == F_filesystem_quota_block || status == F_prohibited || status == F_input_output) { + entry->status = F_access; + } + else if (status == F_interrupted) { + entry->status = F_interrupted; + } + else { + entry->status = F_invalid; + } + + return F_false; } - return F_none; + entry->status = F_none; + return F_true; } -#endif // _di_controller_entry_list_read_ +#endif // _di_controller_entry_read_ #ifdef __cplusplus } // extern "C" diff --git a/level_3/controller/c/private-entry.h b/level_3/controller/c/private-entry.h index 10d7993..e5edbec 100644 --- a/level_3/controller/c/private-entry.h +++ b/level_3/controller/c/private-entry.h @@ -12,26 +12,26 @@ #ifndef _di_controller_entry_cache_t_ typedef struct { - f_string_length_t line_item; + f_string_length_t line_action; f_string_length_t line_list; - f_string_range_t range_item; f_string_range_t range_list; f_fss_comments_t comments; f_fss_delimits_t delimits; - f_fss_content_t content_item; - f_fss_contents_t contents_items; - f_fss_contents_t contents_list; - f_fss_objects_t objects_items; - f_fss_objects_t objects_list; + f_fss_content_t content_action; + f_fss_contents_t content_actions; + f_fss_contents_t content_items; + f_fss_objects_t object_actions; + f_fss_objects_t object_items; - f_string_dynamic_t buffer_entry; + f_string_dynamic_t buffer_file; + f_string_dynamic_t buffer_item; - f_string_static_t name_entry; + f_string_dynamic_t name_action; + f_string_dynamic_t name_file; f_string_dynamic_t name_item; - f_string_dynamic_t name_list; } controller_entry_cache_t; #define controller_entry_cache_t_initialize \ @@ -39,7 +39,6 @@ 0, \ 0, \ f_string_range_t_initialize, \ - f_string_range_t_initialize, \ f_fss_comments_t_initialize, \ f_fss_delimits_t_initialize, \ f_fss_content_t_initialize, \ @@ -48,7 +47,8 @@ f_fss_objects_t_initialize, \ f_fss_objects_t_initialize, \ f_string_dynamic_t_initialize, \ - f_string_static_t_initialize, \ + f_string_dynamic_t_initialize, \ + f_string_dynamic_t_initialize, \ f_string_dynamic_t_initialize, \ f_string_dynamic_t_initialize, \ } @@ -57,13 +57,15 @@ f_macro_fss_comments_t_delete_simple(cache.comments) \ f_macro_fss_delimits_t_delete_simple(cache.delimits) \ f_macro_fss_content_t_delete_simple(cache.content_action) \ - f_macro_fss_contents_t_delete_simple(cache.contents_action) \ - f_macro_fss_contents_t_delete_simple(cache.contents_items) \ - f_macro_fss_objects_t_delete_simple(cache.objects_action) \ - f_macro_fss_objects_t_delete_simple(cache.objects_items) \ - f_macro_string_dynamic_t_delete_simple(cache.buffer_entry) \ - f_macro_string_dynamic_t_delete_simple(cache.name_item) \ - f_macro_string_dynamic_t_delete_simple(cache.name_list) + f_macro_fss_contents_t_delete_simple(cache.content_actions) \ + f_macro_fss_contents_t_delete_simple(cache.content_items) \ + f_macro_fss_objects_t_delete_simple(cache.object_actions) \ + f_macro_fss_objects_t_delete_simple(cache.object_items) \ + f_macro_string_dynamic_t_delete_simple(cache.buffer_file) \ + f_macro_string_dynamic_t_delete_simple(cache.buffer_item) \ + f_macro_string_dynamic_t_delete_simple(cache.name_action) \ + f_macro_string_dynamic_t_delete_simple(cache.name_file) \ + f_macro_string_dynamic_t_delete_simple(cache.name_item) #endif // _di_controller_entry_cache_t_ #ifdef __cplusplus @@ -71,7 +73,67 @@ extern "C" { #endif /** - * Increase the size of the entry lists array by the specified amount, but only if necessary. + * Increase the size of the entry item actions array by the specified amount, but only if necessary. + * + * This only increases size if the current used plus amount is greater than the currently allocated size. + * + * @param amount + * A positive number representing how much to increase the size by. + * @param actions + * The entry item actions to resize. + * + * @return + * F_none on success. + * F_array_too_large on success, but requested size is too small (resize is smaller than requested length). + * + * Errors (with error bit) from: f_memory_resize(). + * + * @see f_memory_resize() + */ +#ifndef _di_controller_entry_actions_increase_by_ + extern f_return_status controller_entry_actions_increase_by(const f_array_length_t amount, controller_entry_actions_t *actions) f_gcc_attribute_visibility_internal; +#endif // _di_controller_entry_actions_increase_by_ + +/** + * Read the entry list, extracting all items and values. + * + * @param data + * The program data. + * @param setting + * The controller settings data. + * @param content_range + * The range in the list buffer representing the content. + * @param cache + * A structure for containing and caching relevant data. + * @param list + * The processed list. + * + * @return + * F_none on success. + */ +#ifndef _di_controller_entry_actions_read_ + extern f_return_status controller_entry_actions_read(const controller_data_t data, const controller_setting_t setting, const f_string_range_t content_range, controller_entry_cache_t *cache, controller_entry_actions_t *items) f_gcc_attribute_visibility_internal; +#endif // _di_controller_entry_actions_read_ + +/** + * Print additional error/warning information in addition to existing error. + * + * This is explicitly intended to be used in addition to the error message. + * + * @param output + * The error or warning output structure. + * @param cache + * A structure for containing and caching relevant data. + * + * @see controller_entry_actions_read() + * @see controller_entry_read() + */ +#ifndef _di_controller_entry_error_print_ + extern void controller_entry_error_print(const fll_error_print_t output, const controller_entry_cache_t cache) f_gcc_attribute_visibility_internal; +#endif // _di_controller_entry_error_print_ + +/** + * Increase the size of the entry items array by the specified amount, but only if necessary. * * This only increases size if the current used plus amount is greater than the currently allocated size. * @@ -88,12 +150,12 @@ extern "C" { * * @see f_memory_resize() */ -#ifndef _di_controller_entry_lists_increase_by_ - extern f_return_status controller_entry_lists_increase_by(const f_array_length_t amount, controller_entry_lists_t *lists) f_gcc_attribute_visibility_internal; -#endif // _di_controller_entry_lists_increase_by_ +#ifndef _di_controller_entry_items_increase_by_ + extern f_return_status controller_entry_items_increase_by(const f_array_length_t amount, controller_entry_items_t *lists) f_gcc_attribute_visibility_internal; +#endif // _di_controller_entry_items_increase_by_ /** - * Read the entry file, extracting all valid entry lists. + * Read the entry, extracting all lists. * * @param data * The program data. @@ -102,19 +164,19 @@ extern "C" { * @param entry_name * The string identifying the entry. * This is constructed from the path parts to the file without the file extension and without the settings directory prefix. - * "/etc/controller/entries/my.entry" would have a entry name of "my". + * "/etc/controller/entries/example/my.entry" would have a rule id of "example/my". * @param cache * A structure for containing and caching relevant data. - * @param lists - * The processed list. + * @param entry + * The processed entry. * * @return - * F_none on success. - * @todo determine how the error management will be done for both entry and rule (ideally the "controller" should avoid failing if possible given that it is the defacto "init" program). + * F_true on success. + * F_false on failure. */ -#ifndef _di_controller_entry_list_read_ - extern f_return_status controller_entry_list_read(const controller_data_t data, const controller_setting_t setting, const f_string_static_t entry_name, controller_entry_cache_t *cache, controller_entry_lists_t *lists) f_gcc_attribute_visibility_internal; -#endif // _di_controller_entry_list_read_ +#ifndef _di_controller_entry_read_ + extern f_return_status controller_entry_read(const controller_data_t data, const controller_setting_t setting, const f_string_static_t entry_name, controller_entry_cache_t *cache, controller_entry_t *entry) f_gcc_attribute_visibility_internal; +#endif // _di_controller_entry_read_ #ifdef __cplusplus } // extern "C" diff --git a/level_3/controller/c/private-rule.c b/level_3/controller/c/private-rule.c index e54c85e..0729dd9 100644 --- a/level_3/controller/c/private-rule.c +++ b/level_3/controller/c/private-rule.c @@ -65,7 +65,7 @@ extern "C" { status = F_array_too_large; } - const f_status_t status_resize = f_memory_resize((void **) & actions->array, sizeof(controller_rule_actions_t), actions->size, size); + const f_status_t status_resize = f_memory_resize((void **) & actions->array, sizeof(controller_rule_action_t), actions->size, size); if (F_status_is_error(status_resize)) return status_resize; actions->size = size; @@ -81,7 +81,7 @@ extern "C" { // "script" types use the entire content and can be directly passed through. if (item->type == controller_rule_item_type_script) { - status = controller_rule_actions_increase_by(f_memory_default_allocation_step, actions); + status = controller_rule_actions_increase_by(controller_default_allocation_step, actions); if (F_status_is_error(status)) { fll_error_print(data.error, F_status_set_fine(status), "controller_rule_actions_increase_by", F_true); @@ -111,8 +111,8 @@ extern "C" { if (actions->type == controller_rule_action_type_extended_list) { cache->comments.used = 0; cache->delimits.used = 0; - cache->contents_action.used = 0; - cache->objects_action.used = 0; + cache->content_actions.used = 0; + cache->object_actions.used = 0; status = fl_fss_extended_list_content_read(cache->buffer_item, range, &cache->content_action, &cache->delimits, &cache->comments); @@ -120,7 +120,7 @@ extern "C" { fll_error_print(data.error, F_status_set_fine(status), "fl_fss_extended_list_content_read", F_true); } else if (status == FL_fss_found_content) { - status = fll_fss_extended_read(cache->buffer_item, &cache->content_action.array[0], &cache->objects_action, &cache->contents_action, 0, 0, &cache->delimits, 0); + status = fll_fss_extended_read(cache->buffer_item, &cache->content_action.array[0], &cache->object_actions, &cache->content_actions, 0, 0, &cache->delimits, 0); if (F_status_is_error(status)) { fll_error_print(data.error, F_status_set_fine(status), "fll_fss_extended_read", F_true); @@ -135,9 +135,9 @@ extern "C" { f_array_length_t i = 0; f_array_length_t j = 0; - for (; i < cache->objects_action.used; ++i) { + for (; i < cache->object_actions.used; ++i) { - status = f_fss_count_lines(cache->buffer_item, cache->objects_action.array[i].start, &actions->array[actions->used].line); + status = f_fss_count_lines(cache->buffer_item, cache->object_actions.array[i].start, &actions->array[actions->used].line); if (F_status_is_error(status)) { fll_error_print(data.error, F_status_set_fine(status), "f_fss_count_lines", F_true); @@ -154,7 +154,7 @@ extern "C" { break; } - status = controller_rule_action_read(data, cache->buffer_item, &cache->objects_action.array[i], &cache->contents_action.array[i], &actions->array[actions->used]); + status = controller_rule_action_read(data, cache->buffer_item, &cache->object_actions.array[i], &cache->content_actions.array[i], &actions->array[actions->used]); if (F_status_is_error(status)) { fll_error_print(data.error, F_status_set_fine(status), "controller_rule_action_read", F_true); @@ -185,7 +185,7 @@ extern "C" { fll_error_print(data.error, F_status_set_fine(status), "fl_fss_apply_delimit", F_true); } else { - status = controller_rule_actions_increase_by(f_memory_default_allocation_step, actions); + status = controller_rule_actions_increase_by(controller_default_allocation_step, actions); if (F_status_is_error(status)) { fll_error_print(data.error, F_status_set_fine(status), "controller_rule_actions_increase_by", F_true); @@ -244,7 +244,7 @@ extern "C" { } if (cache.name_item.used) { - fprintf(output.to.stream, "%s '", item ? "item" : "setting"); + fprintf(output.to.stream, "rule %s '", item ? "item" : "setting"); fprintf(output.to.stream, "%s%s%s%s", output.context.after->string, output.notable.before->string, cache.name_item.string, output.notable.after->string); fprintf(output.to.stream, "%s' on line ", output.context.before->string); fprintf(output.to.stream, "%s%s%llu%s", output.context.after->string, output.notable.before->string, cache.line_item, output.notable.after->string); @@ -263,7 +263,6 @@ extern "C" { f_status_t status = F_none; f_string_range_t range = f_macro_string_range_t_initialize(cache->buffer_item.used); - f_fss_quote_t quote = 0; controller_rule_actions_t *actions = 0; @@ -299,7 +298,7 @@ extern "C" { cache->delimits.used = 0; // The current line is not an Extended List object, so the next possibility is a Basic List (and Extended List, both use the same Object structure). - status = fl_fss_extended_object_read(cache->buffer_item, &range, &cache->range_action, "e, &cache->delimits); + status = fl_fss_extended_object_read(cache->buffer_item, &range, &cache->range_action, 0, &cache->delimits); if (F_status_is_error(status)) { fll_error_print(data.error, F_status_set_fine(status), "fl_fss_extended_object_read", F_true); @@ -381,7 +380,7 @@ extern "C" { else { if (data.warning.verbosity == f_console_verbosity_debug) { fprintf(data.warning.to.stream, "%c", f_string_eol[0]); - fprintf(data.warning.to.stream, "%s%sUnknown action type.%s%c", data.warning.context.before->string, data.warning.prefix ? data.warning.prefix : "", data.warning.context.after->string, f_string_eol[0]); + fprintf(data.warning.to.stream, "%s%sUnknown rule item action type.%s%c", data.warning.context.before->string, data.warning.prefix ? data.warning.prefix : "", data.warning.context.after->string, f_string_eol[0]); controller_rule_error_print(data.warning, *cache, F_true); } @@ -393,7 +392,7 @@ extern "C" { if (actions->intent == controller_rule_action_intent_create || actions->intent == controller_rule_action_intent_group || actions->intent == controller_rule_action_intent_use || actions->intent == controller_rule_action_intent_user) { if (data.error.verbosity != f_console_verbosity_quiet) { fprintf(data.error.to.stream, "%c", f_string_eol[0]); - fprintf(data.error.to.stream, "%s%sFSS Extended List is not allowed for this item action type.%s%c", data.error.context.before->string, data.error.prefix ? data.error.prefix : "", data.error.context.after->string, f_string_eol[0]); + fprintf(data.error.to.stream, "%s%sFSS Extended List is not allowed for this rule item action type.%s%c", data.error.context.before->string, data.error.prefix ? data.error.prefix : "", data.error.context.after->string, f_string_eol[0]); } status = F_status_set_error(F_unsupported); @@ -429,7 +428,7 @@ extern "C" { status = F_array_too_large; } - const f_status_t status_resize = f_memory_resize((void **) & items->array, sizeof(controller_rule_items_t), items->size, size); + const f_status_t status_resize = f_memory_resize((void **) & items->array, sizeof(controller_rule_item_t), items->size, size); if (F_status_is_error(status_resize)) return status_resize; items->size = size; @@ -458,12 +457,25 @@ extern "C" { rule->items.used = 0; + cache->line_item = 0; + cache->line_action = 0; + + cache->range_action.start = 1; + cache->range_action.stop = 0; + cache->comments.used = 0; cache->delimits.used = 0; - cache->buffer_items.used = 0; - cache->contents_items.used = 0; - cache->objects_items.used = 0; + cache->buffer_file.used = 0; + cache->buffer_item.used = 0; + + cache->content_items.used = 0; + + cache->object_items.used = 0; + + cache->name_action.used = 0; + cache->name_file.used = 0; + cache->name_item.used = 0; status = fl_string_dynamic_append_nulless(rule_id, &rule->id); @@ -471,46 +483,19 @@ extern "C" { fll_error_print(data.error, F_status_set_fine(status), "fl_string_dynamic_append_nulless", F_true); } else { - f_file_t file = f_file_t_initialize; - - const f_string_length_t file_path_length = setting.path_setting.used + f_path_separator_length + controller_string_rules_length + f_path_separator_length + rule->id.used + f_path_separator_length; - char file_path[file_path_length + 1]; - - memcpy(file_path, setting.path_setting.string, setting.path_setting.used); - memcpy(file_path + setting.path_setting.used + f_path_separator_length, controller_string_rules, controller_string_rules_length); - memcpy(file_path + setting.path_setting.used + f_path_separator_length + controller_string_rules_length + f_path_separator_length, rule->id.string, rule->id.used); - - file_path[setting.path_setting.used] = f_path_separator[0]; - file_path[setting.path_setting.used + f_path_separator_length + controller_string_rules_length] = f_path_separator[0]; - file_path[file_path_length - 1] = f_path_separator[0]; - file_path[file_path_length] = 0; - - status = f_file_stream_open(file_path, 0, &file); - - if (F_status_is_error(status)) { - fll_error_file_print(data.error, F_status_set_fine(status), "f_file_stream_open", F_true, rule->id.string, "open", fll_error_file_type_file); - } - else { - status = f_file_stream_read(file, 1, &cache->buffer_items); - - if (F_status_is_error(status)) { - fll_error_file_print(data.error, F_status_set_fine(status), "f_file_stream_read", F_true, cache->name_file.string, "read", fll_error_file_type_file); - } - } - - f_file_stream_close(F_true, &file); + status = controller_file_load(data, setting, controller_string_rules, rule->id, controller_string_rule, controller_string_rules_length, controller_string_rule_length, &cache->buffer_file, &cache->name_file); } - if (F_status_is_error_not(status) && cache->buffer_items.used) { - f_string_range_t range = f_macro_string_range_t_initialize(cache->buffer_items.used); + if (F_status_is_error_not(status) && cache->buffer_file.used) { + f_string_range_t range = f_macro_string_range_t_initialize(cache->buffer_file.used); - status = fll_fss_basic_list_read(cache->buffer_items, &range, &cache->objects_items, &cache->contents_items, &cache->delimits, 0, &cache->comments); + status = fll_fss_basic_list_read(cache->buffer_file, &range, &cache->object_items, &cache->content_items, &cache->delimits, 0, &cache->comments); if (F_status_is_error(status)) { fll_error_print(data.error, F_status_set_fine(status), "fll_fss_basic_list_read", F_true); } else { - status = fl_fss_apply_delimit(cache->delimits, &cache->buffer_items); + status = fl_fss_apply_delimit(cache->delimits, &cache->buffer_file); if (F_status_is_error(status)) { fll_error_print(data.error, F_status_set_fine(status), "fl_fss_apply_delimit", F_true); @@ -518,38 +503,37 @@ extern "C" { } } - if (F_status_is_error_not(status) && cache->objects_items.used) { - status = controller_rule_items_increase_by(cache->objects_items.used, &rule->items); + if (F_status_is_error_not(status) && cache->object_items.used) { + status = controller_rule_items_increase_by(cache->object_items.used, &rule->items); if (F_status_is_error(status)) { fll_error_print(data.error, F_status_set_fine(status), "controller_rule_items_increase_by", F_true); } else { - for (f_array_length_t i = 0; i < cache->objects_items.used; ++i) { + for (f_array_length_t i = 0; i < cache->object_items.used; ++i) { cache->line_item = 0; cache->line_action = 0; cache->range_action.start = 1; cache->range_action.stop = 0; - cache->range_item = cache->objects_items.array[i]; cache->comments.used = 0; cache->delimits.used = 0; cache->content_action.used = 0; - cache->contents_action.used = 0; - cache->objects_action.used = 0; + cache->content_actions.used = 0; + + cache->object_actions.used = 0; cache->buffer_item.used = 0; cache->name_action.used = 0; - cache->name_file.used = 0; cache->name_item.used = 0; for_item = F_true; - status = f_fss_count_lines(cache->buffer_items, cache->range_item.start, &cache->line_item); + status = f_fss_count_lines(cache->buffer_file, cache->object_items.array[i].start, &cache->line_item); if (F_status_is_error(status)) { fll_error_print(data.error, F_status_set_fine(status), "f_fss_count_lines", F_true); @@ -558,7 +542,7 @@ extern "C" { rule->items.array[rule->items.used].line = cache->line_item; - status = fl_string_dynamic_rip_nulless(cache->buffer_items, cache->range_item, &cache->name_item); + status = fl_string_dynamic_rip_nulless(cache->buffer_file, cache->object_items.array[i], &cache->name_item); if (F_status_is_error(status)) { fll_error_print(data.error, F_status_set_fine(status), "fl_string_dynamic_rip_nulless", F_true); @@ -572,13 +556,6 @@ extern "C" { break; } - status = fl_string_dynamic_partial_append(cache->buffer_items, cache->contents_items.array[i].array[0], &cache->buffer_item); - - if (F_status_is_error(status)) { - fll_error_print(data.error, F_status_set_fine(status), "fl_string_dynamic_partial_append", F_true); - break; - } - if (fl_string_dynamic_compare_string(controller_string_settings, cache->name_item, controller_string_settings_length) == F_equal_to) { rule->items.array[rule->items.used].type = 0; } @@ -594,7 +571,7 @@ extern "C" { else { if (data.warning.verbosity == f_console_verbosity_debug) { fprintf(data.warning.to.stream, "%c", f_string_eol[0]); - fprintf(data.warning.to.stream, "%s%sUnknown item type.%s%c", data.warning.context.before->string, data.warning.prefix ? data.warning.prefix : "", data.warning.context.after->string, f_string_eol[0]); + fprintf(data.warning.to.stream, "%s%sUnknown rule item type.%s%c", data.warning.context.before->string, data.warning.prefix ? data.warning.prefix : "", data.warning.context.after->string, f_string_eol[0]); controller_rule_error_print(data.warning, *cache, F_true); } @@ -602,6 +579,13 @@ extern "C" { continue; } + status = fl_string_dynamic_partial_append(cache->buffer_file, cache->content_items.array[i].array[0], &cache->buffer_item); + + if (F_status_is_error(status)) { + fll_error_print(data.error, F_status_set_fine(status), "fl_string_dynamic_partial_append", F_true); + break; + } + if (rule->items.array[rule->items.used].type) { status = controller_rule_item_read(data, cache, &rule->items.array[rule->items.used]); if (F_status_is_error(status)) break; @@ -660,9 +644,7 @@ extern "C" { f_string_range_t range = f_macro_string_range_t_initialize(cache->buffer_item.used); f_string_range_t range2 = f_string_range_t_initialize; - f_fss_quote_t quote = 0; - - status = fll_fss_extended_read(cache->buffer_item, &range, &cache->objects_action, &cache->contents_action, 0, 0, &cache->delimits, 0); + status = fll_fss_extended_read(cache->buffer_item, &range, &cache->object_actions, &cache->content_actions, 0, 0, &cache->delimits, 0); if (F_status_is_error(status)) { fll_error_print(data.error, F_status_set_fine(status), "fll_fss_extended_read", F_true); @@ -679,11 +661,11 @@ extern "C" { f_array_length_t j = 0; uint8_t type = 0; - for (; i < cache->contents_action.used; ++i, type = 0) { + for (; i < cache->content_actions.used; ++i, type = 0) { // name_item is used to store the setting name. cache->name_item.used = 0; - status = fl_string_dynamic_partial_append_nulless(cache->buffer_item, cache->objects_action.array[i], &cache->name_item); + status = fl_string_dynamic_partial_append_nulless(cache->buffer_item, cache->object_actions.array[i], &cache->name_item); if (F_status_is_error(status)) { fll_error_print(data.error, F_status_set_fine(status), "fl_string_dynamic_partial_append_nulless", F_true); @@ -735,7 +717,7 @@ extern "C" { continue; } - if (!cache->contents_action.array[i].used) { + if (!cache->content_actions.array[i].used) { if (data.warning.verbosity == f_console_verbosity_debug) { fprintf(data.warning.to.stream, "%c", f_string_eol[0]); fprintf(data.warning.to.stream, "%s%sEmpty rule setting.%s%c", data.warning.context.before->string, data.warning.prefix ? data.warning.prefix : "", data.warning.context.after->string, f_string_eol[0]); @@ -749,8 +731,8 @@ extern "C" { // name_action is used to store all setting values for a single setting. cache->name_action.used = 0; - range2.start = cache->contents_action.array[i].array[0].start; - range2.stop = cache->contents_action.array[i].array[cache->contents_action.array[i].used - 1].stop; + range2.start = cache->content_actions.array[i].array[0].start; + range2.stop = cache->content_actions.array[i].array[cache->content_actions.array[i].used - 1].stop; status = fl_string_dynamic_partial_append_nulless(cache->buffer_item, range2, &cache->name_action); @@ -771,7 +753,7 @@ extern "C" { } if (type == 1) { - if (cache->contents_action.array[i].used != 2) { + if (cache->content_actions.array[i].used != 2) { fprintf(data.error.to.stream, "%c", f_string_eol[0]); fprintf(data.error.to.stream, "%s%sRule setting requires exactly two Content.%s%c", data.error.context.before->string, data.error.prefix ? data.error.prefix : "", data.error.context.after->string, f_string_eol[0]); @@ -804,7 +786,7 @@ extern "C" { continue; } - status = fl_string_dynamic_partial_append_nulless(cache->buffer_item, cache->contents_action.array[i].array[0], &rule->defines.array[rule->defines.used].name); + status = fl_string_dynamic_partial_append_nulless(cache->buffer_item, cache->content_actions.array[i].array[0], &rule->defines.array[rule->defines.used].name); if (F_status_is_error(status)) { fll_error_print(data.error, F_status_set_fine(status), "fl_string_dynamic_partial_append_nulless", F_true); @@ -821,7 +803,7 @@ extern "C" { continue; } - status = fl_string_dynamic_partial_append_nulless(cache->buffer_item, cache->contents_action.array[i].array[1], &rule->defines.array[rule->defines.used].value); + status = fl_string_dynamic_partial_append_nulless(cache->buffer_item, cache->content_actions.array[i].array[1], &rule->defines.array[rule->defines.used].value); if (F_status_is_error(status)) { fll_error_print(data.error, F_status_set_fine(status), "fl_string_dynamic_partial_append_nulless", F_true); @@ -853,7 +835,7 @@ extern "C" { } if (type == 6) { - if (setting_value->used || cache->contents_action.array[i].used != 1) { + if (setting_value->used || cache->content_actions.array[i].used != 1) { fprintf(data.error.to.stream, "%c", f_string_eol[0]); fprintf(data.error.to.stream, "%s%sRule setting requires exactly one Content.%s%c", data.error.context.before->string, data.error.prefix ? data.error.prefix : "", data.error.context.after->string, f_string_eol[0]); @@ -867,7 +849,7 @@ extern "C" { } } else { - if (setting_value->used || cache->contents_action.array[i].used > 1) { + if (setting_value->used || cache->content_actions.array[i].used > 1) { fprintf(data.error.to.stream, "%c", f_string_eol[0]); fprintf(data.error.to.stream, "%s%sRule setting requires no Content or exactly one Content.%s%c", data.error.context.before->string, data.error.prefix ? data.error.prefix : "", data.error.context.after->string, f_string_eol[0]); @@ -880,13 +862,13 @@ extern "C" { continue; } - if (!cache->contents_action.array[i].used) { + if (!cache->content_actions.array[i].used) { setting_value->used = 0; continue; } } - status = fl_string_dynamic_partial_append_nulless(cache->buffer_item, cache->contents_action.array[i].array[0], setting_value); + status = fl_string_dynamic_partial_append_nulless(cache->buffer_item, cache->content_actions.array[i].array[0], setting_value); if (F_status_is_error(status)) { fll_error_print(data.error, F_status_set_fine(status), "fl_string_dynamic_partial_append_nulless", F_true); @@ -990,7 +972,7 @@ extern "C" { setting_values = &rule->wish; } - for (j = 0; j < cache->contents_action.array[i].used; ++j) { + for (j = 0; j < cache->content_actions.array[i].used; ++j) { status = fl_string_dynamics_increase(setting_values); @@ -1011,7 +993,7 @@ extern "C" { setting_values->array[setting_values->used].used = 0; - status = fl_string_dynamic_partial_append_nulless(cache->buffer_item, cache->contents_action.array[i].array[j], &setting_values->array[setting_values->used]); + status = fl_string_dynamic_partial_append_nulless(cache->buffer_item, cache->content_actions.array[i].array[j], &setting_values->array[setting_values->used]); if (F_status_is_error(status)) { fll_error_print(data.error, F_status_set_fine(status), "fl_string_dynamic_partial_append_nulless", F_true); diff --git a/level_3/controller/c/private-rule.h b/level_3/controller/c/private-rule.h index 203d73b..f4a6f6c 100644 --- a/level_3/controller/c/private-rule.h +++ b/level_3/controller/c/private-rule.h @@ -20,19 +20,18 @@ extern "C" { f_string_length_t line_item; f_string_range_t range_action; - f_string_range_t range_item; f_fss_comments_t comments; f_fss_delimits_t delimits; f_fss_content_t content_action; - f_fss_contents_t contents_action; - f_fss_contents_t contents_items; - f_fss_objects_t objects_action; - f_fss_objects_t objects_items; + f_fss_contents_t content_actions; + f_fss_contents_t content_items; + f_fss_objects_t object_actions; + f_fss_objects_t object_items; + f_string_dynamic_t buffer_file; f_string_dynamic_t buffer_item; - f_string_dynamic_t buffer_items; f_string_dynamic_t name_action; f_string_dynamic_t name_file; @@ -44,7 +43,6 @@ extern "C" { 0, \ 0, \ f_string_range_t_initialize, \ - f_string_range_t_initialize, \ f_fss_comments_t_initialize, \ f_fss_delimits_t_initialize, \ f_fss_content_t_initialize, \ @@ -63,12 +61,12 @@ extern "C" { f_macro_fss_comments_t_delete_simple(cache.comments) \ f_macro_fss_delimits_t_delete_simple(cache.delimits) \ f_macro_fss_content_t_delete_simple(cache.content_action) \ - f_macro_fss_contents_t_delete_simple(cache.contents_action) \ - f_macro_fss_contents_t_delete_simple(cache.contents_items) \ - f_macro_fss_objects_t_delete_simple(cache.objects_action) \ - f_macro_fss_objects_t_delete_simple(cache.objects_items) \ + f_macro_fss_contents_t_delete_simple(cache.content_actions) \ + f_macro_fss_contents_t_delete_simple(cache.content_items) \ + f_macro_fss_objects_t_delete_simple(cache.object_actions) \ + f_macro_fss_objects_t_delete_simple(cache.object_items) \ + f_macro_string_dynamic_t_delete_simple(cache.buffer_file) \ f_macro_string_dynamic_t_delete_simple(cache.buffer_item) \ - f_macro_string_dynamic_t_delete_simple(cache.buffer_items) \ f_macro_string_dynamic_t_delete_simple(cache.name_action) \ f_macro_string_dynamic_t_delete_simple(cache.name_file) \ f_macro_string_dynamic_t_delete_simple(cache.name_item) @@ -171,7 +169,10 @@ extern "C" { * If FALSE, then this error is associated with a rule setting. * * @see controller_rule_actions_read() + * @see controller_rule_item_read() * @see controller_rule_items_read() + * @see controller_rule_read() + * @see controller_rule_setting_read() */ #ifndef _di_controller_rule_error_print_ void controller_rule_error_print(const fll_error_print_t output, const controller_rule_cache_t cache, const bool item) f_gcc_attribute_visibility_internal; @@ -237,7 +238,7 @@ extern "C" { * @param rule_id * 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/boot/my.rule" would have a rule id of "boot/my". + * "/etc/controller/rules/example/my.rule" would have a rule id of "example/my". * @param cache * A structure for containing and caching relevant data. * @param rule diff --git a/level_3/controller/documents/entry.txt b/level_3/controller/documents/entry.txt index 8a75037..f2fb838 100644 --- a/level_3/controller/documents/entry.txt +++ b/level_3/controller/documents/entry.txt @@ -10,21 +10,29 @@ Entry Documentation: All other Basic List Objects are not executed unless either a "category" or a "failsafe" specifies the Object. Execution of all Basic List Objects is top-down. - Inside each list there are additional commands: "consider", "failsafe", "group", "rule", and "timeout". + Inside each list there are additional commands: "consider", "failsafe", "item", "ready", "rule", and "timeout". The "consider" command is a special case of a "rule" command. All available Content are identical. - The difference is that this rule is only processed at this spot at this time if and when some "rule" command (or a "consider" command is determined to be executed) designates that this consideration is required (via "require"), wanted (via "want"), or wished (via "wish"). + The difference is that "consider" is only processed at this spot at this time if and when some "rule" command (or another "consider" command is determined to be executed) designates that this consideration is required (via "require"), wanted (via "want"), or wished (via "wish"). If this is determined to be executed, then this is immediately executed in the designated position and applies all properties as appropriate (such as "asynchronous", for example). + If this is determined not to be executed, then this "consider" is ignored as if it was never there in the first place. The "failsafe" command accepts only a valid Basic List Object in which will be executed when a failure is detected. Only a single "failsafe" command may exist at a time. Each time a "failsafe" command is specified, it replaces the previous "failsafe" command. - The "group" command accepts only a valid Basic List Object in which will be immediately executed. - A "group" is another list within the entry file to execute in it entirety. + The "item" command accepts only a valid Basic List Object in which will be immediately executed. + An "item" is another list within the entry file to execute in it entirety. Any valid Object name, except for the reserved "main", may be called using this. + The "ready" command instructs the controller program when it is safe to perform normal tasks, such as creating the pid file. + When not specified, the state is always assumed to be ready. + For example, the controller may be used as a full blown "init" replacement and therefore may need to mount the /var/run/ directory. + If the pid file is created at program start, then the /var/run/controller.pid would be written before the /var/run/ directory is properly mounted. + This could be a problem, such as on a read-only filesystem the pid creation fails and controller bails out on error. + Adding ready essentially specifies a point in time in the rules in which things are expected to be safe for such basic operations. + The "rule" command immediately executes a given rule file. The first Content represents the rule ID, which is a relative path the rule file is to be found, without the file extension. - Do not include leading or trailing slashes in the name. diff --git a/level_3/controller/specifications/entry.txt b/level_3/controller/specifications/entry.txt index e060a82..48f59ba 100644 --- a/level_3/controller/specifications/entry.txt +++ b/level_3/controller/specifications/entry.txt @@ -1,7 +1,7 @@ # fss-0002 Entry Specification: - The "rule" files follow the FSS-0005 (Somewhat Basic List) format. + The "entry" files follow the FSS-0005 (Somewhat Basic List) format. An entry file name is expected to have the file extension ".entry". @@ -19,7 +19,8 @@ Entry Specification: - require - wait "failsafe": One Content that is a valid Object name, except for the reserved "main". - "group": One Content that is a valid Object name, except for the reserved "main". + "item": One Content that is a valid Object name, except for the reserved "main". + "ready": Zero Content. "rule": One or more Content. The first Content that is the relative directory path (without any leading/trailing slashes). The second Content that is the basename for a rule file. -- 1.8.3.1