From e531e93735c1de3739ecf1074f25ed843b1b6d22 Mon Sep 17 00:00:00 2001 From: Kevin Day Date: Sat, 26 Dec 2020 17:01:32 -0600 Subject: [PATCH] Progress: scheduler support in contol program. The control (aka cgroups) will be implemented in an isolated commit as I suspect the required changes will be more extensive. --- level_1/fl_execute/c/execute-common.h | 10 +- level_2/fll_execute/c/execute.h | 1 + level_2/fll_execute/c/private-execute.c | 5 +- level_3/controller/c/controller.h | 2 + level_3/controller/c/private-common.h | 24 ++- level_3/controller/c/private-rule.c | 163 +++++++++++++++++++-- .../settings/example/rules/command/multiple.rule | 1 + .../data/settings/rules/service/logger.rule | 1 + level_3/controller/documents/rule.txt | 7 +- level_3/controller/specifications/rule.txt | 2 +- 10 files changed, 190 insertions(+), 26 deletions(-) diff --git a/level_1/fl_execute/c/execute-common.h b/level_1/fl_execute/c/execute-common.h index e6be62a..455b5b7 100644 --- a/level_1/fl_execute/c/execute-common.h +++ b/level_1/fl_execute/c/execute-common.h @@ -54,22 +54,22 @@ extern "C" { /** * A structure representing a scheduler and its parameters for execution. * - * policy: the scheduler policy. - * parameter: a pointer to the scheduler parameters; + * policy: the scheduler policy. + * priority: the scheduler priority; */ #ifndef _di_fl_execute_scheduler_t_ typedef struct { int policy; - const struct sched_param *parameter; + int priority; } fl_execute_scheduler_t; #define fl_execute_scheduler_t_initialize { 0, 0 } - #define fl_macro_execute_scheduler_t_initialize(policy, parameter) { policy, parameter } + #define fl_macro_execute_scheduler_t_initialize(policy, priority) { policy, priority } #define fl_execute_scheduler_t_clear(scheduler) \ scheduler.policy = 0; \ - scheduler.parameter = 0; + scheduler.priority = 0; #endif // _di_fl_execute_scheduler_t_ /** diff --git a/level_2/fll_execute/c/execute.h b/level_2/fll_execute/c/execute.h index 2ba0dfb..817a859 100644 --- a/level_2/fll_execute/c/execute.h +++ b/level_2/fll_execute/c/execute.h @@ -15,6 +15,7 @@ // libc includes #include +#include #include #include #include diff --git a/level_2/fll_execute/c/private-execute.c b/level_2/fll_execute/c/private-execute.c index b424159..fdb45f3 100644 --- a/level_2/fll_execute/c/private-execute.c +++ b/level_2/fll_execute/c/private-execute.c @@ -136,9 +136,12 @@ extern "C" { if (as.scheduler) { const int process_id = getpid(); + struct sched_param parameter_schedule; + parameter_schedule.sched_priority = as.scheduler->priority; + errno = 0; - if (sched_setscheduler(process_id, as.scheduler->policy, as.scheduler->parameter) == -1) { + if (sched_setscheduler(process_id, as.scheduler->policy, ¶meter_schedule) == -1) { *result = -1; if (parameter && parameter->option & fl_execute_parameter_option_exit) { diff --git a/level_3/controller/c/controller.h b/level_3/controller/c/controller.h index a59dafb..4c4c826 100644 --- a/level_3/controller/c/controller.h +++ b/level_3/controller/c/controller.h @@ -20,8 +20,10 @@ #ifndef _controller_h // libc includes +#include #include #include +#include #include // fll-0 includes diff --git a/level_3/controller/c/private-common.h b/level_3/controller/c/private-common.h index ad9aba9..58a5ddc 100644 --- a/level_3/controller/c/private-common.h +++ b/level_3/controller/c/private-common.h @@ -17,11 +17,13 @@ extern "C" { #define controller_string_actions "actions" #define controller_string_asynchronous "asynchronous" #define controller_string_bash "bash" + #define controller_string_batch "batch" #define controller_string_capability "capability" #define controller_string_create "create" #define controller_string_command "command" #define controller_string_consider "consider" #define controller_string_control "control" + #define controller_string_deadline "deadline" #define controller_string_default "default" #define controller_string_define "define" #define controller_string_entry "entry" @@ -29,9 +31,11 @@ extern "C" { #define controller_string_environment "environment" #define controller_string_fail "fail" #define controller_string_failsafe "failsafe" + #define controller_string_fifo "fifo" #define controller_string_group "group" #define controller_string_groups "groups" #define controller_string_how "how" + #define controller_string_idle "idle" #define controller_string_item "item" #define controller_string_kill "kill" #define controller_string_main "main" @@ -41,6 +45,7 @@ extern "C" { #define controller_string_nice "nice" #define controller_string_no "no" #define controller_string_optional "optional" + #define controller_string_other "other" #define controller_string_parameter "parameter" #define controller_string_path "path" #define controller_string_pid "pid" @@ -50,6 +55,7 @@ extern "C" { #define controller_string_require "require" #define controller_string_required "required" #define controller_string_restart "restart" + #define controller_string_round_robin "round_robin" #define controller_string_rule "rule" #define controller_string_rules "rules" #define controller_string_scheduler "scheduler" @@ -73,11 +79,13 @@ extern "C" { #define controller_string_actions_length 7 #define controller_string_asynchronous_length 12 #define controller_string_bash_length 4 + #define controller_string_batch_length 5 #define controller_string_capability_length 10 #define controller_string_create_length 6 #define controller_string_command_length 7 #define controller_string_consider_length 8 #define controller_string_control_length 7 + #define controller_string_deadline_length 8 #define controller_string_define_length 6 #define controller_string_default_length 7 #define controller_string_entry_length 5 @@ -85,8 +93,10 @@ extern "C" { #define controller_string_environment_length 11 #define controller_string_fail_length 4 #define controller_string_failsafe_length 8 + #define controller_string_fifo_length 4 #define controller_string_group_length 5 #define controller_string_how_length 3 + #define controller_string_idle_length 4 #define controller_string_item_length 4 #define controller_string_kill_length 4 #define controller_string_main_length 4 @@ -96,6 +106,7 @@ extern "C" { #define controller_string_nice_length 4 #define controller_string_no_length 2 #define controller_string_optional_length 8 + #define controller_string_other_length 5 #define controller_string_parameter_length 9 #define controller_string_path_length 4 #define controller_string_pid_length 3 @@ -105,6 +116,7 @@ extern "C" { #define controller_string_require_length 7 #define controller_string_required_length 8 #define controller_string_restart_length 7 + #define controller_string_round_robin_length 11 #define controller_string_rule_length 4 #define controller_string_rules_length 5 #define controller_string_scheduler_length 9 @@ -272,9 +284,10 @@ extern "C" { #define controller_rule_option_wait 0x8 // bitwise codes representing properties on controller_rule_t that have been found in the rule file. - #define controller_rule_has_group 0x1 - #define controller_rule_has_nice 0x2 - #define controller_rule_has_user 0x4 + #define controller_rule_has_group 0x1 + #define controller_rule_has_nice 0x2 + #define controller_rule_has_scheduler 0x4 + #define controller_rule_has_user 0x8 typedef struct { f_status_t status; @@ -295,7 +308,6 @@ extern "C" { f_string_dynamic_t name; f_string_dynamic_t control; f_string_dynamic_t path; - f_string_dynamic_t scheduler; f_string_dynamic_t script; f_string_maps_t define; @@ -308,6 +320,7 @@ extern "C" { f_capability_t capability; f_int32s_t groups; + fl_execute_scheduler_t scheduler; controller_rule_items_t items; } controller_rule_t; @@ -329,7 +342,6 @@ extern "C" { f_string_dynamic_t_initialize, \ f_string_dynamic_t_initialize, \ f_string_dynamic_t_initialize, \ - f_string_dynamic_t_initialize, \ f_string_maps_t_initialize, \ f_string_maps_t_initialize, \ f_string_dynamics_t_initialize, \ @@ -338,6 +350,7 @@ extern "C" { f_string_dynamics_t_initialize, \ f_capability_t_initialize, \ f_int32s_t_initialize, \ + fl_execute_scheduler_t_initialize, \ controller_rule_items_initialize, \ } @@ -346,7 +359,6 @@ extern "C" { fl_string_dynamic_delete(&rule.name); \ fl_string_dynamic_delete(&rule.control); \ fl_string_dynamic_delete(&rule.path); \ - fl_string_dynamic_delete(&rule.scheduler); \ fl_string_dynamic_delete(&rule.script); \ f_macro_string_maps_t_delete_simple(rule.define) \ f_macro_string_maps_t_delete_simple(rule.parameter) \ diff --git a/level_3/controller/c/private-rule.c b/level_3/controller/c/private-rule.c index 83198ca..cb67233 100644 --- a/level_3/controller/c/private-rule.c +++ b/level_3/controller/c/private-rule.c @@ -480,8 +480,8 @@ extern "C" { as.capability = rule->capability; } - if (rule->scheduler.used) { - // @todo: as.scheduler = + if (rule->has & controller_rule_has_scheduler) { + as.scheduler = &rule->scheduler; } if (rule->control.used) { @@ -1366,7 +1366,8 @@ extern "C" { rule->name.used = 0; rule->control.used = 0; rule->path.used = 0; - rule->scheduler.used = 0; + rule->scheduler.policy = 0; + rule->scheduler.priority = 0; rule->script.used = 0; rule->define.used = 0; @@ -1826,7 +1827,7 @@ extern "C" { continue; } - if (type == controller_rule_setting_type_control || type == controller_rule_setting_type_name || type == controller_rule_setting_type_path || type == controller_rule_setting_type_scheduler || type == controller_rule_setting_type_script) { + if (type == controller_rule_setting_type_control || type == controller_rule_setting_type_name || type == controller_rule_setting_type_path || type == controller_rule_setting_type_script) { if (type == controller_rule_setting_type_control) { setting_value = &rule->control; } @@ -1836,9 +1837,6 @@ extern "C" { else if (type == controller_rule_setting_type_path) { setting_value = &rule->path; } - else if (type == controller_rule_setting_type_scheduler) { - setting_value = &rule->scheduler; - } else if (type == controller_rule_setting_type_script) { setting_value = &rule->script; } @@ -1950,10 +1948,124 @@ extern "C" { continue; } } - else if (type == controller_rule_setting_type_scheduler) { - // @todo + + continue; + } + + + if (type == controller_rule_setting_type_scheduler) { + + if (cache->content_actions.array[i].used < 1 || cache->content_actions.array[i].used > 2 || rule->has & controller_rule_has_scheduler) { + + if (data.error.verbosity != f_console_verbosity_quiet) { + fprintf(data.error.to.stream, "%c", f_string_eol_s[0]); + fprintf(data.error.to.stream, "%s%sRule setting requires either one or two Content.%s%c", data.error.context.before->string, data.error.prefix ? data.error.prefix : f_string_empty_s, data.error.context.after->string, f_string_eol_s[0]); + + controller_rule_error_print(data.error, *cache, F_false); + } + + if (F_status_is_error_not(status_return)) { + status_return = F_status_set_error(F_valid_not); + } + + continue; } + if (fl_string_dynamic_partial_compare_string(controller_string_batch, cache->buffer_item, controller_string_batch_length, cache->content_actions.array[i].array[0]) == F_equal_to) { + rule->scheduler.policy = SCHED_BATCH; + rule->scheduler.priority = 0; + } + else if (fl_string_dynamic_partial_compare_string(controller_string_deadline, cache->buffer_item, controller_string_deadline_length, cache->content_actions.array[i].array[0]) == F_equal_to) { + rule->scheduler.policy = SCHED_DEADLINE; + rule->scheduler.priority = 49; + } + else if (fl_string_dynamic_partial_compare_string(controller_string_fifo, cache->buffer_item, controller_string_fifo_length, cache->content_actions.array[i].array[0]) == F_equal_to) { + rule->scheduler.policy = SCHED_FIFO; + rule->scheduler.priority = 49; + } + else if (fl_string_dynamic_partial_compare_string(controller_string_idle, cache->buffer_item, controller_string_idle_length, cache->content_actions.array[i].array[0]) == F_equal_to) { + rule->scheduler.policy = SCHED_IDLE; + rule->scheduler.priority = 0; + } + else if (fl_string_dynamic_partial_compare_string(controller_string_other, cache->buffer_item, controller_string_other_length, cache->content_actions.array[i].array[0]) == F_equal_to) { + rule->scheduler.policy = SCHED_OTHER; + rule->scheduler.priority = 0; + } + else if (fl_string_dynamic_partial_compare_string(controller_string_round_robin, cache->buffer_item, controller_string_round_robin_length, cache->content_actions.array[i].array[0]) == F_equal_to) { + rule->scheduler.policy = SCHED_RR; + rule->scheduler.priority = 49; + } + else { + if (data.error.verbosity != f_console_verbosity_quiet) { + fprintf(data.error.to.stream, "%c", f_string_eol_s[0]); + fprintf(data.error.to.stream, "%s%sRule setting has an unknown scheduler '", data.error.context.before->string, data.error.prefix ? data.error.prefix : f_string_empty_s); + fprintf(data.error.to.stream, "%s%s", data.error.context.after->string, data.error.notable.before->string); + f_print_dynamic_partial(data.error.to.stream, cache->buffer_item, cache->content_actions.array[i].array[0]); + fprintf(data.error.to.stream, "%s%s'.%s%c", data.error.notable.after->string, data.error.context.before->string, data.error.context.after->string, f_string_eol_s[0]); + + controller_rule_error_print(data.error, *cache, F_false); + } + + if (F_status_is_error_not(status_return)) { + status_return = F_status_set_error(F_valid_not); + } + + continue; + } + + if (cache->content_actions.array[i].used > 1) { + const bool zero_only = rule->scheduler.policy == SCHED_BATCH || rule->scheduler.policy == SCHED_IDLE || rule->scheduler.policy == SCHED_OTHER; + + f_number_signed_t number = 0; + + status = fl_conversion_string_to_number_signed(cache->buffer_item.string, &number, cache->content_actions.array[i].array[1]); + + if (F_status_is_error(status) || (zero_only && number) || (!zero_only && (number < 1 || number > 99))) { + status = F_status_set_fine(status); + + if ((zero_only && number) || (!zero_only && (number < 1 || number > 99)) || status == F_data_not || status == F_number || status == F_number_overflow) { + + if (data.error.verbosity != f_console_verbosity_quiet) { + fprintf(data.error.to.stream, "%c", f_string_eol_s[0]); + fprintf(data.error.to.stream, "%s%sRule setting has an invalid number '", data.error.context.before->string, data.error.prefix ? data.error.prefix : f_string_empty_s); + fprintf(data.error.to.stream, "%s%s", data.error.context.after->string, data.error.notable.before->string); + f_print_dynamic_partial(data.error.to.stream, cache->buffer_item, cache->content_actions.array[i].array[1]); + + if (zero_only) { + fprintf(data.error.to.stream, "%s%s', only ", data.error.notable.after->string, data.error.context.before->string); + fprintf(data.error.to.stream, "%s%s0%s", data.error.context.after->string, data.error.notable.before->string, data.error.notable.after->string); + fprintf(data.error.to.stream, "%s is", data.error.context.before->string); + } + else { + fprintf(data.error.to.stream, "%s%s', only the whole numbers inclusively between ", data.error.notable.after->string, data.error.context.before->string); + fprintf(data.error.to.stream, "%s%s1%s", data.error.context.after->string, data.error.notable.before->string, data.error.notable.after->string); + fprintf(data.error.to.stream, "%s and ", data.error.context.before->string); + fprintf(data.error.to.stream, "%s%s99%s", data.error.context.after->string, data.error.notable.before->string, data.error.notable.after->string); + fprintf(data.error.to.stream, "%s are", data.error.context.before->string); + } + + fprintf(data.error.to.stream, " allowed for the designated scheduler.%s%c", data.error.context.after->string, f_string_eol_s[0]); + + controller_rule_error_print(data.error, *cache, F_false); + } + + if (F_status_is_error_not(status_return)) { + status_return = F_status_set_error(F_valid_not); + } + } + else { + fll_error_print(data.error, status, "fl_conversion_string_to_number_signed", F_true); + status = F_status_set_error(status); + } + + continue; + } + + rule->scheduler.priority = number; + } + + rule->has |= controller_rule_has_scheduler; + continue; } @@ -2050,8 +2162,7 @@ extern "C" { fprintf(data.error.to.stream, "%s%sRule setting has an invalid number '", data.error.context.before->string, data.error.prefix ? data.error.prefix : f_string_empty_s); fprintf(data.error.to.stream, "%s%s", data.error.context.after->string, data.error.notable.before->string); f_print_dynamic_partial(data.error.to.stream, cache->buffer_item, cache->content_actions.array[i].array[0]); - fprintf(data.error.to.stream, "%s", data.error.notable.after->string); - fprintf(data.error.to.stream, "%s', only the whole numbers inclusively between ", data.error.context.before->string); + fprintf(data.error.to.stream, "%s%s', only the whole numbers inclusively between ", data.error.notable.after->string, data.error.context.before->string); fprintf(data.error.to.stream, "%s%s-20%s", data.error.context.after->string, data.error.notable.before->string, data.error.notable.after->string); fprintf(data.error.to.stream, "%s and ", data.error.context.before->string); fprintf(data.error.to.stream, "%s%s19%s", data.error.context.after->string, data.error.notable.before->string, data.error.notable.after->string); @@ -2514,7 +2625,35 @@ extern "C" { fprintf(data.output.stream, " %s%s%s %s%c", data.context.set.important.before->string, controller_string_control, data.context.set.important.after->string, rule->control.used ? rule->control.string : f_string_empty_s, f_string_eol_s[0]); fprintf(data.output.stream, " %s%s%s %s%c", data.context.set.important.before->string, controller_string_path, data.context.set.important.after->string, rule->path.used ? rule->path.string : f_string_empty_s, f_string_eol_s[0]); - fprintf(data.output.stream, " %s%s%s %s%c", data.context.set.important.before->string, controller_string_scheduler, data.context.set.important.after->string, rule->scheduler.used ? rule->scheduler.string : f_string_empty_s, f_string_eol_s[0]); + + + fprintf(data.output.stream, " %s%s%s", data.context.set.important.before->string, controller_string_scheduler, data.context.set.important.after->string); + if (rule->has & controller_rule_has_scheduler) { + f_string_t policy = ""; + + if (rule->scheduler.policy == SCHED_BATCH) { + policy = controller_string_batch; + } + else if (rule->scheduler.policy == SCHED_DEADLINE) { + policy = controller_string_deadline; + } + else if (rule->scheduler.policy == SCHED_FIFO) { + policy = controller_string_fifo; + } + else if (rule->scheduler.policy == SCHED_IDLE) { + policy = controller_string_idle; + } + else if (rule->scheduler.policy == SCHED_OTHER) { + policy = controller_string_other; + } + else if (rule->scheduler.policy == SCHED_RR) { + policy = controller_string_round_robin; + } + + fprintf(data.output.stream, " %s %i", policy, rule->scheduler.priority); + } + fprintf(data.output.stream, "%c", f_string_eol_s[0]); + fprintf(data.output.stream, " %s%s%s %s%c", data.context.set.important.before->string, controller_string_script, data.context.set.important.after->string, rule->script.used ? rule->script.string : f_string_empty_s, f_string_eol_s[0]); fprintf(data.output.stream, " %s%s%s", data.context.set.important.before->string, controller_string_nice, data.context.set.important.after->string); 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 b2a2bcb..da9ca3f 100644 --- a/level_3/controller/data/settings/example/rules/command/multiple.rule +++ b/level_3/controller/data/settings/example/rules/command/multiple.rule @@ -4,6 +4,7 @@ setting: name "Multiple Commands: id, whoami, date, etc.." capability "all=" nice 15 + scheduler batch 0 #user kevin #group list 8 root diff --git a/level_3/controller/data/settings/rules/service/logger.rule b/level_3/controller/data/settings/rules/service/logger.rule index 4c65092..23abe8f 100644 --- a/level_3/controller/data/settings/rules/service/logger.rule +++ b/level_3/controller/data/settings/rules/service/logger.rule @@ -7,6 +7,7 @@ setting: name "System Logger" capability "all=" nice 19 + scheduler idle service: # @todo consider adding support for IKI to make "/var/run/logger/logger.pid" a variable. diff --git a/level_3/controller/documents/rule.txt b/level_3/controller/documents/rule.txt index 9a1aa02..1fdf144 100644 --- a/level_3/controller/documents/rule.txt +++ b/level_3/controller/documents/rule.txt @@ -20,7 +20,7 @@ Rule Documentation: "parameter": A statically defined IKI name and its associated value for use in this rule file. "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. + "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. @@ -42,6 +42,11 @@ Rule Documentation: 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". + The "command" Rule Type provides a simple command to run under the different circumstances: "start", "stop", "restart", and "reload". A "command" always operates in the foreground. diff --git a/level_3/controller/specifications/rule.txt b/level_3/controller/specifications/rule.txt index a7ae7ae..5df9b62 100644 --- a/level_3/controller/specifications/rule.txt +++ b/level_3/controller/specifications/rule.txt @@ -38,7 +38,7 @@ Rule Specification: "nice": One Content, must be a valid number for process "niceness" (Any whole number inclusively between -20 to 19). "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 Content representing a scheduler name. + "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"). -- 1.8.3.1