This includes some significant structural re-organization of the controller project.
-#include "../controller.h"
-#include "../private-common.h"
+#include "../controller/controller.h"
+#include "private-common.h"
#ifdef __cplusplus
extern "C" {
--- /dev/null
+#include "../controller/controller.h"
+#include "private-common.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef _di_controller_string_s_
+ const f_string_t controller_action_s = CONTROLLER_action_s;
+ const f_string_t controller_actions_s = CONTROLLER_actions_s;
+ const f_string_t controller_affinity_s = CONTROLLER_affinity_s;
+ const f_string_t controller_as_s = CONTROLLER_as_s;
+ const f_string_t controller_asynchronous_s = CONTROLLER_asynchronous_s;
+ const f_string_t controller_bash_s = CONTROLLER_bash_s;
+ const f_string_t controller_batch_s = CONTROLLER_batch_s;
+ const f_string_t controller_capability_s = CONTROLLER_capability_s;
+ const f_string_t controller_cgroup_s = CONTROLLER_cgroup_s;
+ const f_string_t controller_create_s = CONTROLLER_create_s;
+ const f_string_t controller_command_s = CONTROLLER_command_s;
+ const f_string_t controller_consider_s = CONTROLLER_consider_s;
+ const f_string_t controller_control_s = CONTROLLER_control_s;
+ const f_string_t controller_control_group_s = CONTROLLER_control_group_s;
+ const f_string_t controller_control_mode_s = CONTROLLER_control_mode_s;
+ const f_string_t controller_control_user_s = CONTROLLER_control_user_s;
+ const f_string_t controller_cpu_s = CONTROLLER_cpu_s;
+ const f_string_t controller_core_s = CONTROLLER_core_s;
+ const f_string_t controller_data_s = CONTROLLER_data_s;
+ const f_string_t controller_deadline_s = CONTROLLER_deadline_s;
+ const f_string_t controller_default_s = CONTROLLER_default_s;
+ const f_string_t controller_define_s = CONTROLLER_define_s;
+ const f_string_t controller_delay_s = CONTROLLER_delay_s;
+ const f_string_t controller_disable_s = CONTROLLER_disable_s;
+ const f_string_t controller_entry_s = CONTROLLER_entry_s;
+ const f_string_t controller_entries_s = CONTROLLER_entries_s;
+ const f_string_t controller_environment_s = CONTROLLER_environment_s;
+ const f_string_t controller_error_s = CONTROLLER_error_s;
+ const f_string_t controller_execute_s = CONTROLLER_execute_s;
+ const f_string_t controller_existing_s = CONTROLLER_existing_s;
+ const f_string_t controller_exit_s = CONTROLLER_exit_s;
+ const f_string_t controller_exits_s = CONTROLLER_exits_s;
+ const f_string_t controller_fail_s = CONTROLLER_fail_s;
+ const f_string_t controller_failsafe_s = CONTROLLER_failsafe_s;
+ const f_string_t controller_failure_s = CONTROLLER_failure_s;
+ const f_string_t controller_fifo_s = CONTROLLER_fifo_s;
+ const f_string_t controller_freeze_s = CONTROLLER_freeze_s;
+ const f_string_t controller_fsize_s = CONTROLLER_fsize_s;
+ const f_string_t controller_full_path_s = CONTROLLER_full_path_s;
+ const f_string_t controller_group_s = CONTROLLER_group_s;
+ const f_string_t controller_groups_s = CONTROLLER_groups_s;
+ const f_string_t controller_how_s = CONTROLLER_how_s;
+ const f_string_t controller_idle_s = CONTROLLER_idle_s;
+ const f_string_t controller_item_s = CONTROLLER_item_s;
+ const f_string_t controller_init_s = CONTROLLER_init_s;
+ const f_string_t controller_kill_s = CONTROLLER_kill_s;
+ const f_string_t controller_length_s = CONTROLLER_length_s;
+ const f_string_t controller_limit_s = CONTROLLER_limit_s;
+ const f_string_t controller_locks_s = CONTROLLER_locks_s;
+ const f_string_t controller_main_s = CONTROLLER_main_s;
+ const f_string_t controller_max_s = CONTROLLER_max_s;
+ const f_string_t controller_memlock_s = CONTROLLER_memlock_s;
+ const f_string_t controller_method_s = CONTROLLER_method_s;
+ const f_string_t controller_mode_s = CONTROLLER_mode_s;
+ const f_string_t controller_msgqueue_s = CONTROLLER_msgqueue_s;
+ const f_string_t controller_name_s = CONTROLLER_name_s;
+ const f_string_t controller_need_s = CONTROLLER_need_s;
+ const f_string_t controller_new_s = CONTROLLER_new_s;
+ const f_string_t controller_nice_s = CONTROLLER_nice_s;
+ const f_string_t controller_no_s = CONTROLLER_no_s;
+ const f_string_t controller_nofile_s = CONTROLLER_nofile_s;
+ const f_string_t controller_normal_s = CONTROLLER_normal_s;
+ const f_string_t controller_nproc_s = CONTROLLER_nproc_s;
+ const f_string_t controller_on_s = CONTROLLER_on_s;
+ const f_string_t controller_optional_s = CONTROLLER_optional_s;
+ const f_string_t controller_other_s = CONTROLLER_other_s;
+ const f_string_t controller_parameter_s = CONTROLLER_parameter_s;
+ const f_string_t controller_parameters_s = CONTROLLER_parameters_s;
+ const f_string_t controller_path_s = CONTROLLER_path_s;
+ const f_string_t controller_pause_s = CONTROLLER_pause_s;
+ const f_string_t controller_payload_type_s = CONTROLLER_payload_type_s;
+ const f_string_t controller_pid_s = CONTROLLER_pid_s;
+ const f_string_t controller_pid_file_s = CONTROLLER_pid_file_s;
+ const f_string_t controller_processor_s = CONTROLLER_processor_s;
+ const f_string_t controller_program_s = CONTROLLER_program_s;
+ const f_string_t controller_ready_s = CONTROLLER_ready_s;
+ const f_string_t controller_reload_s = CONTROLLER_reload_s;
+ const f_string_t controller_require_s = CONTROLLER_require_s;
+ const f_string_t controller_required_s = CONTROLLER_required_s;
+ const f_string_t controller_rerun_s = CONTROLLER_rerun_s;
+ const f_string_t controller_reset_s = CONTROLLER_reset_s;
+ const f_string_t controller_restart_s = CONTROLLER_restart_s;
+ const f_string_t controller_resume_s = CONTROLLER_resume_s;
+ const f_string_t controller_round_robin_s = CONTROLLER_round_robin_s;
+ const f_string_t controller_rss_s = CONTROLLER_rss_s;
+ const f_string_t controller_rtprio_s = CONTROLLER_rtprio_s;
+ const f_string_t controller_rttime_s = CONTROLLER_rttime_s;
+ const f_string_t controller_rule_s = CONTROLLER_rule_s;
+ const f_string_t controller_rules_s = CONTROLLER_rules_s;
+ const f_string_t controller_same_s = CONTROLLER_same_s;
+ const f_string_t controller_scheduler_s = CONTROLLER_scheduler_s;
+ const f_string_t controller_script_s = CONTROLLER_script_s;
+ const f_string_t controller_service_s = CONTROLLER_service_s;
+ const f_string_t controller_setting_s = CONTROLLER_setting_s;
+ const f_string_t controller_session_s = CONTROLLER_session_s;
+ const f_string_t controller_session_new_s = CONTROLLER_session_new_s;
+ const f_string_t controller_session_same_s = CONTROLLER_session_same_s;
+ const f_string_t controller_show_s = CONTROLLER_show_s;
+ const f_string_t controller_sigpending_s = CONTROLLER_sigpending_s;
+ const f_string_t controller_stack_s = CONTROLLER_stack_s;
+ const f_string_t controller_start_s = CONTROLLER_start_s;
+ const f_string_t controller_status_s = CONTROLLER_status_s;
+ const f_string_t controller_stop_s = CONTROLLER_stop_s;
+ const f_string_t controller_succeed_s = CONTROLLER_succeed_s;
+ const f_string_t controller_success_s = CONTROLLER_success_s;
+ const f_string_t controller_synchronous_s = CONTROLLER_synchronous_s;
+ const f_string_t controller_thaw_s = CONTROLLER_thaw_s;
+ const f_string_t controller_timeout_s = CONTROLLER_timeout_s;
+ const f_string_t controller_type_s = CONTROLLER_type_s;
+ const f_string_t controller_use_s = CONTROLLER_use_s;
+ const f_string_t controller_user_s = CONTROLLER_user_s;
+ const f_string_t controller_utility_s = CONTROLLER_utility_s;
+ const f_string_t controller_value_s = CONTROLLER_value_s;
+ const f_string_t controller_wait_s = CONTROLLER_wait_s;
+ const f_string_t controller_want_s = CONTROLLER_want_s;
+ const f_string_t controller_wish_s = CONTROLLER_wish_s;
+ const f_string_t controller_with_s = CONTROLLER_with_s;
+ const f_string_t controller_yes_s = CONTROLLER_yes_s;
+#endif // _di_controller_string_s_
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
--- /dev/null
+/**
+ * FLL - Level 3
+ *
+ * Project: Controller
+ * API Version: 0.5
+ * Licenses: lgpl-2.1-or-later
+ */
+#ifndef _PRIVATE_common_h
+#define _PRIVATE_common_h
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// These are included in the order of their dependencies.
+#include "private-cache.h"
+#include "private-execute_set.h"
+#include "private-lock.h"
+#include "private-rule.h"
+#include "private-task.h"
+#include "private-process.h"
+#include "private-entry.h"
+#include "private-setting.h"
+#include "private-thread.h"
+#include "private-state.h"
+#include "private-control.h"
+
+/**
+ * All special strings used within this program.
+ *
+ * These are generally the names to match, representing some action or setting.
+ */
+#ifndef _di_controller_string_s_
+ #define CONTROLLER_action_s "action"
+ #define CONTROLLER_actions_s "actions"
+ #define CONTROLLER_affinity_s "affinity"
+ #define CONTROLLER_as_s "as"
+ #define CONTROLLER_asynchronous_s "asynchronous"
+ #define CONTROLLER_bash_s "bash"
+ #define CONTROLLER_batch_s "batch"
+ #define CONTROLLER_capability_s "capability"
+ #define CONTROLLER_cgroup_s "cgroup"
+ #define CONTROLLER_create_s "create"
+ #define CONTROLLER_command_s "command"
+ #define CONTROLLER_consider_s "consider"
+ #define CONTROLLER_control_s "control"
+ #define CONTROLLER_control_group_s "control_group"
+ #define CONTROLLER_control_mode_s "control_mode"
+ #define CONTROLLER_control_user_s "control_user"
+ #define CONTROLLER_cpu_s "cpu"
+ #define CONTROLLER_core_s "core"
+ #define CONTROLLER_data_s "data"
+ #define CONTROLLER_deadline_s "deadline"
+ #define CONTROLLER_default_s "default"
+ #define CONTROLLER_define_s "define"
+ #define CONTROLLER_delay_s "delay"
+ #define CONTROLLER_disable_s "disable"
+ #define CONTROLLER_entry_s "entry"
+ #define CONTROLLER_entries_s "entries"
+ #define CONTROLLER_environment_s "environment"
+ #define CONTROLLER_error_s "error"
+ #define CONTROLLER_execute_s "execute"
+ #define CONTROLLER_existing_s "existing"
+ #define CONTROLLER_exit_s "exit"
+ #define CONTROLLER_exits_s "exits"
+ #define CONTROLLER_fail_s "fail"
+ #define CONTROLLER_failsafe_s "failsafe"
+ #define CONTROLLER_failure_s "failure"
+ #define CONTROLLER_fifo_s "fifo"
+ #define CONTROLLER_freeze_s "freeze"
+ #define CONTROLLER_fsize_s "fsize"
+ #define CONTROLLER_full_path_s "full_path"
+ #define CONTROLLER_group_s "group"
+ #define CONTROLLER_groups_s "groups"
+ #define CONTROLLER_how_s "how"
+ #define CONTROLLER_idle_s "idle"
+ #define CONTROLLER_item_s "item"
+ #define CONTROLLER_init_s "init"
+ #define CONTROLLER_kill_s "kill"
+ #define CONTROLLER_length_s "length"
+ #define CONTROLLER_limit_s "limit"
+ #define CONTROLLER_locks_s "locks"
+ #define CONTROLLER_main_s "main"
+ #define CONTROLLER_max_s "max"
+ #define CONTROLLER_memlock_s "memlock"
+ #define CONTROLLER_method_s "method"
+ #define CONTROLLER_mode_s "mode"
+ #define CONTROLLER_msgqueue_s "msgqueue"
+ #define CONTROLLER_name_s "name"
+ #define CONTROLLER_need_s "need"
+ #define CONTROLLER_new_s "new"
+ #define CONTROLLER_nice_s "nice"
+ #define CONTROLLER_no_s "no"
+ #define CONTROLLER_nofile_s "nofile"
+ #define CONTROLLER_normal_s "normal"
+ #define CONTROLLER_nproc_s "nproc"
+ #define CONTROLLER_on_s "on"
+ #define CONTROLLER_optional_s "optional"
+ #define CONTROLLER_other_s "other"
+ #define CONTROLLER_parameter_s "parameter"
+ #define CONTROLLER_parameters_s "parameters"
+ #define CONTROLLER_path_s "path"
+ #define CONTROLLER_pause_s "pause"
+ #define CONTROLLER_payload_type_s "# fss-000e\n"
+ #define CONTROLLER_pid_s "pid"
+ #define CONTROLLER_pid_file_s "pid_file"
+ #define CONTROLLER_processor_s "processor"
+ #define CONTROLLER_program_s "program"
+ #define CONTROLLER_ready_s "ready"
+ #define CONTROLLER_reload_s "reload"
+ #define CONTROLLER_require_s "require"
+ #define CONTROLLER_required_s "required"
+ #define CONTROLLER_rerun_s "rerun"
+ #define CONTROLLER_reset_s "reset"
+ #define CONTROLLER_restart_s "restart"
+ #define CONTROLLER_resume_s "resume"
+ #define CONTROLLER_round_robin_s "round_robin"
+ #define CONTROLLER_rss_s "rss"
+ #define CONTROLLER_rtprio_s "rtprio"
+ #define CONTROLLER_rttime_s "rttime"
+ #define CONTROLLER_rule_s "rule"
+ #define CONTROLLER_rules_s "rules"
+ #define CONTROLLER_same_s "same"
+ #define CONTROLLER_scheduler_s "scheduler"
+ #define CONTROLLER_script_s "script"
+ #define CONTROLLER_service_s "service"
+ #define CONTROLLER_session_s "session"
+ #define CONTROLLER_session_new_s "session_new"
+ #define CONTROLLER_session_same_s "session_same"
+ #define CONTROLLER_setting_s "setting"
+ #define CONTROLLER_sigpending_s "sigpending"
+ #define CONTROLLER_show_s "show"
+ #define CONTROLLER_stack_s "stack"
+ #define CONTROLLER_start_s "start"
+ #define CONTROLLER_status_s "status"
+ #define CONTROLLER_stop_s "stop"
+ #define CONTROLLER_succeed_s "succeed"
+ #define CONTROLLER_success_s "success"
+ #define CONTROLLER_synchronous_s "synchronous"
+ #define CONTROLLER_thaw_s "thaw"
+ #define CONTROLLER_timeout_s "timeout"
+ #define CONTROLLER_type_s "type"
+ #define CONTROLLER_use_s "use"
+ #define CONTROLLER_user_s "user"
+ #define CONTROLLER_utility_s "utility"
+ #define CONTROLLER_value_s "value"
+ #define CONTROLLER_wait_s "wait"
+ #define CONTROLLER_want_s "want"
+ #define CONTROLLER_wish_s "wish"
+ #define CONTROLLER_with_s "with"
+ #define CONTROLLER_yes_s "yes"
+
+ #define controller_action_s_length 6
+ #define controller_actions_s_length 7
+ #define controller_affinity_s_length 8
+ #define controller_as_s_length 2
+ #define controller_asynchronous_s_length 12
+ #define controller_bash_s_length 4
+ #define controller_batch_s_length 5
+ #define controller_capability_s_length 10
+ #define controller_cgroup_s_length 6
+ #define controller_create_s_length 6
+ #define controller_command_s_length 7
+ #define controller_consider_s_length 8
+ #define controller_control_s_length 7
+ #define controller_control_group_s_length 13
+ #define controller_control_mode_s_length 12
+ #define controller_control_user_s_length 12
+ #define controller_core_s_length 4
+ #define controller_cpu_s_length 3
+ #define controller_data_s_length 4
+ #define controller_deadline_s_length 8
+ #define controller_default_s_length 7
+ #define controller_define_s_length 6
+ #define controller_delay_s_length 5
+ #define controller_disable_s_length 7
+ #define controller_entry_s_length 5
+ #define controller_entries_s_length 7
+ #define controller_environment_s_length 11
+ #define controller_error_s_length 5
+ #define controller_existing_s_length 8
+ #define controller_execute_s_length 7
+ #define controller_exit_s_length 4
+ #define controller_exits_s_length 5
+ #define controller_fail_s_length 4
+ #define controller_failure_s_length 7
+ #define controller_failsafe_s_length 8
+ #define controller_fifo_s_length 4
+ #define controller_freeze_s_length 6
+ #define controller_fsize_s_length 5
+ #define controller_full_path_s_length 9
+ #define controller_group_s_length 5
+ #define controller_groups_s_length 6
+ #define controller_how_s_length 3
+ #define controller_idle_s_length 4
+ #define controller_init_s_length 4
+ #define controller_item_s_length 4
+ #define controller_kill_s_length 4
+ #define controller_length_s_length 6
+ #define controller_limit_s_length 5
+ #define controller_locks_s_length 5
+ #define controller_main_s_length 4
+ #define controller_max_s_length 3
+ #define controller_memlock_s_length 7
+ #define controller_method_s_length 6
+ #define controller_mode_s_length 4
+ #define controller_msgqueue_s_length 8
+ #define controller_name_s_length 4
+ #define controller_need_s_length 4
+ #define controller_new_s_length 3
+ #define controller_nice_s_length 4
+ #define controller_no_s_length 2
+ #define controller_nofile_s_length 6
+ #define controller_normal_s_length 6
+ #define controller_nproc_s_length 5
+ #define controller_on_s_length 2
+ #define controller_optional_s_length 8
+ #define controller_other_s_length 5
+ #define controller_parameter_s_length 9
+ #define controller_parameters_s_length 10
+ #define controller_path_s_length 4
+ #define controller_pause_s_length 5
+ #define controller_payload_type_s_length 11
+ #define controller_pid_s_length 3
+ #define controller_pid_file_s_length 8
+ #define controller_processor_s_length 9
+ #define controller_program_s_length 7
+ #define controller_ready_s_length 5
+ #define controller_reload_s_length 6
+ #define controller_require_s_length 7
+ #define controller_required_s_length 8
+ #define controller_rerun_s_length 5
+ #define controller_reset_s_length 5
+ #define controller_restart_s_length 7
+ #define controller_resume_s_length 6
+ #define controller_round_robin_s_length 11
+ #define controller_rss_s_length 3
+ #define controller_rtprio_s_length 6
+ #define controller_rttime_s_length 6
+ #define controller_rule_s_length 4
+ #define controller_rules_s_length 5
+ #define controller_same_s_length 4
+ #define controller_scheduler_s_length 9
+ #define controller_script_s_length 6
+ #define controller_service_s_length 7
+ #define controller_session_s_length 7
+ #define controller_session_new_s_length 11
+ #define controller_session_same_s_length 12
+ #define controller_setting_s_length 7
+ #define controller_show_s_length 4
+ #define controller_sigpending_s_length 10
+ #define controller_stack_s_length 5
+ #define controller_start_s_length 5
+ #define controller_status_s_length 6
+ #define controller_stop_s_length 4
+ #define controller_succeed_s_length 7
+ #define controller_success_s_length 7
+ #define controller_synchronous_s_length 11
+ #define controller_thaw_s_length 4
+ #define controller_timeout_s_length 7
+ #define controller_type_s_length 4
+ #define controller_use_s_length 3
+ #define controller_user_s_length 4
+ #define controller_utility_s_length 7
+ #define controller_value_s_length 5
+ #define controller_wait_s_length 4
+ #define controller_want_s_length 4
+ #define controller_wish_s_length 4
+ #define controller_with_s_length 4
+ #define controller_yes_s_length 3
+
+ extern const f_string_t controller_action_s;
+ extern const f_string_t controller_actions_s;
+ extern const f_string_t controller_affinity_s;
+ extern const f_string_t controller_as_s;
+ extern const f_string_t controller_asynchronous_s;
+ extern const f_string_t controller_bash_s;
+ extern const f_string_t controller_batch_s;
+ extern const f_string_t controller_capability_s;
+ extern const f_string_t controller_cgroup_s;
+ extern const f_string_t controller_create_s;
+ extern const f_string_t controller_command_s;
+ extern const f_string_t controller_consider_s;
+ extern const f_string_t controller_control_s;
+ extern const f_string_t controller_control_group_s;
+ extern const f_string_t controller_control_mode_s;
+ extern const f_string_t controller_control_user_s;
+ extern const f_string_t controller_core_s;
+ extern const f_string_t controller_cpu_s;
+ extern const f_string_t controller_data_s;
+ extern const f_string_t controller_deadline_s;
+ extern const f_string_t controller_default_s;
+ extern const f_string_t controller_define_s;
+ extern const f_string_t controller_delay_s;
+ extern const f_string_t controller_disable_s;
+ extern const f_string_t controller_entry_s;
+ extern const f_string_t controller_entries_s;
+ extern const f_string_t controller_environment_s;
+ extern const f_string_t controller_error_s;
+ extern const f_string_t controller_existing_s;
+ extern const f_string_t controller_execute_s;
+ extern const f_string_t controller_exit_s;
+ extern const f_string_t controller_exits_s;
+ extern const f_string_t controller_fail_s;
+ extern const f_string_t controller_failsafe_s;
+ extern const f_string_t controller_failure_s;
+ extern const f_string_t controller_fifo_s;
+ extern const f_string_t controller_freeze_s;
+ extern const f_string_t controller_fsize_s;
+ extern const f_string_t controller_full_path_s;
+ extern const f_string_t controller_group_s;
+ extern const f_string_t controller_groups_s;
+ extern const f_string_t controller_how_s;
+ extern const f_string_t controller_idle_s;
+ extern const f_string_t controller_init_s;
+ extern const f_string_t controller_item_s;
+ extern const f_string_t controller_kill_s;
+ extern const f_string_t controller_length_s;
+ extern const f_string_t controller_limit_s;
+ extern const f_string_t controller_locks_s;
+ extern const f_string_t controller_main_s;
+ extern const f_string_t controller_max_s;
+ extern const f_string_t controller_memlock_s;
+ extern const f_string_t controller_method_s;
+ extern const f_string_t controller_mode_s;
+ extern const f_string_t controller_msgqueue_s;
+ extern const f_string_t controller_name_s;
+ extern const f_string_t controller_need_s;
+ extern const f_string_t controller_new_s;
+ extern const f_string_t controller_nice_s;
+ extern const f_string_t controller_no_s;
+ extern const f_string_t controller_nofile_s;
+ extern const f_string_t controller_normal_s;
+ extern const f_string_t controller_nproc_s;
+ extern const f_string_t controller_on_s;
+ extern const f_string_t controller_optional_s;
+ extern const f_string_t controller_other_s;
+ extern const f_string_t controller_parameter_s;
+ extern const f_string_t controller_parameters_s;
+ extern const f_string_t controller_path_s;
+ extern const f_string_t controller_pause_s;
+ extern const f_string_t controller_payload_type_s;
+ extern const f_string_t controller_pid_s;
+ extern const f_string_t controller_pid_file_s;
+ extern const f_string_t controller_processor_s;
+ extern const f_string_t controller_program_s;
+ extern const f_string_t controller_ready_s;
+ extern const f_string_t controller_reload_s;
+ extern const f_string_t controller_require_s;
+ extern const f_string_t controller_required_s;
+ extern const f_string_t controller_rerun_s;
+ extern const f_string_t controller_reset_s;
+ extern const f_string_t controller_restart_s;
+ extern const f_string_t controller_resume_s;
+ extern const f_string_t controller_round_robin_s;
+ extern const f_string_t controller_rss_s;
+ extern const f_string_t controller_rtprio_s;
+ extern const f_string_t controller_rttime_s;
+ extern const f_string_t controller_rule_s;
+ extern const f_string_t controller_rules_s;
+ extern const f_string_t controller_same_s;
+ extern const f_string_t controller_scheduler_s;
+ extern const f_string_t controller_script_s;
+ extern const f_string_t controller_service_s;
+ extern const f_string_t controller_session_s;
+ extern const f_string_t controller_session_new_s;
+ extern const f_string_t controller_session_same_s;
+ extern const f_string_t controller_setting_s;
+ extern const f_string_t controller_show_s;
+ extern const f_string_t controller_sigpending_s;
+ extern const f_string_t controller_stack_s;
+ extern const f_string_t controller_start_s;
+ extern const f_string_t controller_status_s;
+ extern const f_string_t controller_stop_s;
+ extern const f_string_t controller_succeed_s;
+ extern const f_string_t controller_success_s;
+ extern const f_string_t controller_synchronous_s;
+ extern const f_string_t controller_thaw_s;
+ extern const f_string_t controller_timeout_s;
+ extern const f_string_t controller_type_s;
+ extern const f_string_t controller_use_s;
+ extern const f_string_t controller_user_s;
+ extern const f_string_t controller_utility_s;
+ extern const f_string_t controller_value_s;
+ extern const f_string_t controller_wait_s;
+ extern const f_string_t controller_want_s;
+ extern const f_string_t controller_wish_s;
+ extern const f_string_t controller_with_s;
+ extern const f_string_t controller_yes_s;
+#endif // _di_controller_string_s_
+
+/**
+ * A set of codes for resource limitations.
+ *
+ * This essentally converts the POSIX standard names into a more verbose format.
+ */
+#ifndef _di_controller_resource_limit_t_
+ enum {
+ controller_resource_limit_type_as_e = RLIMIT_AS,
+ controller_resource_limit_type_core_e = RLIMIT_CORE,
+ controller_resource_limit_type_cpu_e = RLIMIT_CPU,
+ controller_resource_limit_type_data_e = RLIMIT_DATA,
+ controller_resource_limit_type_fsize_e = RLIMIT_FSIZE,
+ controller_resource_limit_type_locks_e = RLIMIT_LOCKS,
+ controller_resource_limit_type_memlock_e = RLIMIT_MEMLOCK,
+ controller_resource_limit_type_msgqueue_e = RLIMIT_MSGQUEUE,
+ controller_resource_limit_type_nice_e = RLIMIT_NICE,
+ controller_resource_limit_type_nofile_e = RLIMIT_NOFILE,
+ controller_resource_limit_type_nproc_e = RLIMIT_NPROC,
+ controller_resource_limit_type_rss_e = RLIMIT_RSS,
+ controller_resource_limit_type_rtprio_e = RLIMIT_RTPRIO,
+ controller_resource_limit_type_rttime_e = RLIMIT_RTTIME,
+ controller_resource_limit_type_sigpending_e = RLIMIT_SIGPENDING,
+ controller_resource_limit_type_stack_e = RLIMIT_STACK,
+ };
+#endif // _di_controller_resource_limit_t_
+
+/**
+ * Provide common/generic definitions.
+ *
+ * The controller_common_allocation_large_d or controller_common_allocation_small_d must be at least 2 for this project.
+ *
+ * controller_common_allocation_*:
+ * - large: An allocation step used for buffers that are anticipated to have large buffers.
+ * - small: An allocation step used for buffers that are anticipated to have small buffers.
+ */
+#ifndef _di_controller_common_
+ #define controller_common_allocation_large_d 256
+ #define controller_common_allocation_small_d 16
+#endif // _di_controller_common_
+
+/**
+ * A set of codes representing different with flags.
+ */
+#ifndef _di_controller_with_defines_
+ #define controller_with_full_path_d 0x1
+ #define controller_with_session_new_d 0x2
+ #define controller_with_session_same_d 0x4
+#endif // _di_controller_with_defines_
+
+/**
+ * A wrapper used for passing a common set of all data, particularly for sharing between threads.
+ *
+ * main: The main program data.
+ * setting: All loaded settings.
+ * thread: All thread related data.
+ */
+#ifndef _di_controller_main_t_
+ typedef struct {
+ controller_main_t *main;
+ controller_setting_t *setting;
+ controller_thread_t *thread;
+ } controller_global_t;
+
+ #define controller_global_t_initialize { 0, 0, 0 }
+
+ #define macro_controller_global_t_initialize(main, setting, thread) { \
+ main, \
+ setting, \
+ thread, \
+ }
+#endif // _di_controller_main_t_
+
+/**
+ * A wrapper used for passing a set of entry processing and execution related data.
+ *
+ * global: All data globally shared.
+ * setting: The setting data.
+ */
+#ifndef _di_controller_main_entry_t_
+ typedef struct {
+ controller_global_t *global;
+ controller_setting_t *setting;
+ } controller_main_entry_t;
+
+ #define controller_main_entry_t_initialize { 0, 0 }
+
+ #define macro_controller_main_entry_t_initialize(global, setting) { \
+ global, \
+ setting, \
+ }
+#endif // _di_controller_main_entry_t_
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // _PRIVATE_common_h
-#include "../controller.h"
-#include "../private-common.h"
+#include "../controller/controller.h"
+#include "private-common.h"
#ifdef __cplusplus
extern "C" {
#endif
-#ifndef _di_controller_packet_delete_simple_
- void controller_packet_delete_simple(controller_packet_t * const packet) {
+#ifndef _di_controller_control_delete_simple_
+ void controller_control_delete_simple(controller_control_t * const control) {
- f_string_dynamic_resize(0, &packet->payload);
+ f_string_dynamic_resize(0, &control->cache_1);
+ f_string_dynamic_resize(0, &control->cache_2);
+ f_string_dynamic_resize(0, &control->cache_3);
+
+ f_string_dynamic_resize(0, &control->input);
+ f_string_dynamic_resize(0, &control->output);
}
-#endif // _di_controller_packet_delete_simple_
+#endif // _di_controller_control_delete_simple_
#ifdef __cplusplus
} // extern "C"
* Provide default control settings.
*
* controller_control_default:
- * - socket_backlog: The amount of waiting client connections to support while handling a socket connection.
- * - socket_buffer: The size of the buffer in bytes when processing packets (must not be set smaller the packet headers).
- * - socket_header: The minimum size of the packet header to read to be able to process the size information.
- * - socket_linger: The number of seconds to linger the connection before closing.
- * - socket_timeout: The number of microseconds to wait.
+ * - socket_backlog: The amount of waiting client connections to support while handling a socket connection.
+ * - socket_buffer: The preferred max size of the buffer such that if the buffer exceeds this then it is reallocated to this size at the end of processing.
+ * - socket_buffer_max: The max size allowed in the buffer (this value must not be set smaller than the packet headers).
+ * - socket_cache: The preferred max size of the contol cache such that if the cache exceeds this then it is reallocated to this size at the end of processing.
+ * - socket_header: The minimum size in bytes of the packet header to read to be able to process the size information.
+ * - socket_linger: The number of seconds to linger the connection before closing.
+ * - socket_timeout: The number of microseconds to wait.
+ * - socket_payload_max: The max size allowed for the "payload" part of a packet and must be smaller than socket_buffer_max (this is smaller than socket_buffer_max to allow for enough room to afford a header).
*/
#ifndef _di_controller_control_defaults_
- #define controller_control_default_socket_backlog_d 64
- #define controller_control_default_socket_buffer_d 16384
- #define controller_control_default_socket_header_d 33
- #define controller_control_default_socket_linger_d 1
- #define controller_control_default_socket_timeout_d 10000 // 0.01 seconds.
+ #define controller_control_default_socket_backlog_d 64
+ #define controller_control_default_socket_buffer_d 2048
+ #define controller_control_default_socket_buffer_max_d 4294967296
+ #define controller_control_default_socket_cache_d 128
+ #define controller_control_default_socket_header_d 5
+ #define controller_control_default_socket_linger_d 2
+ #define controller_control_default_socket_timeout_d 10000 // 0.01 seconds.
+ #define controller_control_default_socket_payload_max_d 4294965248
#endif // _di_controller_defaults_
/**
- * A structure representing the packet payload.
+ * A structure for control processing.
*
- * Payload structure: [uint32_t][char X].
- *
- * bytes: The header repesenting the number of bytes in the character payload.
- * payload: The character payload whose size is represented by the header bytes.
+ * server: The server socket connection.
+ * client: The client socket connection.
+ * cache_1: A generic buffer used for caching control related data.
+ * cache_2: A generic buffer used for caching control related data.
+ * input: A buffer used for receiving data from the client.
+ * output: A buffer used for transmitting data to the client.
*/
-#ifndef _di_controller_packet_t_
+#ifndef _di_controller_control_t_
typedef struct {
- uint32_t bytes;
- f_string_dynamic_t payload;
- } controller_packet_t;
+ f_socket_t *server;
+ f_socket_t *client;
+
+ f_string_dynamic_t cache_1;
+ f_string_dynamic_t cache_2;
+ f_string_dynamic_t cache_3;
- #define controller_packet_t_initialize { 0, f_string_dynamic_t_initialize }
+ f_string_dynamic_t input;
+ f_string_dynamic_t output;
+ } controller_control_t;
- #define macro_controller_packet_t_initialize(bytes, payload) { \
- bytes, \
- payload, \
+ #define controller_control_t_initialize { 0, 0, f_string_dynamic_t_initialize, f_string_dynamic_t_initialize, f_string_dynamic_t_initialize, f_string_dynamic_t_initialize, f_string_dynamic_t_initialize }
+
+ #define macro_controller_control_t_initialize(server, client) { \
+ 0, \
+ 0, \
+ f_string_dynamic_t_initialize, \
+ f_string_dynamic_t_initialize, \
+ f_string_dynamic_t_initialize, \
+ f_string_dynamic_t_initialize, \
+ f_string_dynamic_t_initialize, \
}
-#endif // _di_controller_main_t_
+#endif // _di_controller_control_t_
+
+/**
+ * A codes repesent different flags associated with a packet.
+ *
+ * controller_control_packet_flag_*:
+ * - binary: Designate that the packet is in binary mode (when not set then packet is in string mode).
+ * - endian_big: Designate that the packet is in big endian order (when not set then packet is in little endian order).
+ */
+#ifndef _di_controller_control_packet_flag_
+ #define controller_control_packet_flag_binary_d 0x1
+ #define controller_control_packet_flag_endian_big_d 0x2
+#endif // _di_controller_control_packet_flag_
+
+/**
+ * A 34-bit long little-endian structure representing the packet header.
+ *
+ * This represents the packet header which is different from the header inside the packet.
+ *
+ * Generally, the string-based packet format is FSS-000E (Payload).
+ * This format is stored within packet and has it's own header and payload parts.
+ * Example pseudo-structure:
+ * [0][0][4294967296][# fss-000e
+ * header:
+ * type message
+ * length 4294965248
+ * payload:
+ * ...
+ * ]
+ *
+ * This means that string format is "technical" binary because of the header, but after the header it is entirely a string.
+ * Unlike other strings, this string is not NULL terminated and is instead end of tranmsission or end of file terminated (as appropriate).
+ *
+ * type: A boolean that when TRUE designates this as a binary and when FALSE designates this as a string packet.
+ * endian: A boolean designating that when TRUE thath the length digit is big endian and when FALSE designates that the length is little endian.
+ * length: A size representing how large the entire packet is (including the header that is 34 bits).
+ */
/**
- * Fully deallocate all memory for the given packet without caring about return status.
+ * Fully deallocate all memory for the given control data without caring about return status.
*
- * @param packet
- * The packet to deallocate.
+ * @param control
+ * The structure to deallocate.
*/
-#ifndef _di_controller_packet_delete_simple_
- extern void controller_packet_delete_simple(controller_packet_t * const packet) F_attribute_visibility_internal_d;
-#endif // _di_controller_packet_delete_simple_
+#ifndef _di_controller_control_delete_simple_
+ extern void controller_control_delete_simple(controller_control_t * const control) F_attribute_visibility_internal_d;
+#endif // _di_controller_control_delete_simple_
#ifdef __cplusplus
} // extern "C"
-#include "../controller.h"
-#include "../private-common.h"
+#include "../controller/controller.h"
+#include "private-common.h"
#ifdef __cplusplus
extern "C" {
-#include "../controller.h"
-#include "../private-common.h"
+#include "../controller/controller.h"
+#include "private-common.h"
#ifdef __cplusplus
extern "C" {
/**
* A structure for sharing mutexes globally between different threads.
*
- * The print lock is intended to lock any activity printing to stdout/stderr.
* The alert lock is intended for a generic waiting on alerts operations.
+ * The print lock is intended to lock any activity printing to stdout/stderr.
* The process lock is intended to lock any activity on the processs structure.
* The rule lock is intended to lock any activity on the rules structure.
*
- * print: The print mutex lock.
* alert: The alert mutex lock for waking up on alerts.
+ * print: The print mutex lock.
* process: The process r/w lock.
* rule: The rule r/w lock.
* alert_condition: The condition used to trigger alerts.
*/
#ifndef _di_controller_lock_t_
typedef struct {
- f_thread_mutex_t print;
f_thread_mutex_t alert;
+ f_thread_mutex_t print;
f_thread_lock_t process;
f_thread_lock_t rule;
-#include "../controller.h"
-#include "../private-common.h"
+#include "../controller/controller.h"
+#include "private-common.h"
#ifdef __cplusplus
extern "C" {
-#include "../controller.h"
-#include "../private-common.h"
+#include "../controller/controller.h"
+#include "private-common.h"
#ifdef __cplusplus
extern "C" {
-#include "../controller.h"
-#include "../private-common.h"
+#include "../controller/controller.h"
+#include "private-common.h"
#ifdef __cplusplus
extern "C" {
-#include "../controller.h"
-#include "../private-common.h"
+#include "../controller/controller.h"
+#include "private-common.h"
#ifdef __cplusplus
extern "C" {
-#include "../controller.h"
-#include "../private-common.h"
+#include "../controller/controller.h"
+#include "private-common.h"
#ifdef __cplusplus
extern "C" {
--- /dev/null
+#include "../controller/controller.h"
+#include "../common/private-common.h"
+#include "private-control.h"
+#include "../controller/private-controller_print.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef _di_controller_control_accept_
+ f_status_t controller_control_accept(const controller_global_t *global, controller_control_t * const control) {
+
+ f_socket_t client = f_socket_t_initialize;
+
+ control->client = &client;
+
+ f_status_t status = f_socket_accept(&client, control->server->id);
+
+ if (F_status_is_error(status)) {
+ f_socket_disconnect(&client, f_socket_close_fast_e);
+
+ controller_print_error(global->thread, global->main->error, F_status_set_fine(status), "f_socket_accept", F_true);
+
+ return status;
+ }
+
+ controller_control_configure_client(global, &client);
+
+ control->input.used = 0;
+ control->output.used = 0;
+
+ char buffer[controller_control_default_socket_buffer_d + 1];
+ size_t length = 0;
+
+ memset(buffer, 0, controller_control_default_socket_buffer_d + 1);
+
+ // Pre-process the packet header.
+ client.size_read = controller_control_default_socket_header_d;
+ status = f_socket_read(&client, f_socket_flag_peek_d, buffer, &length);
+
+ if (F_status_is_error(status)) {
+ f_socket_disconnect(&client, f_socket_close_fast_e);
+
+ controller_print_error(global->thread, global->main->error, F_status_set_fine(status), "f_socket_read", F_true);
+
+ return status;
+ }
+
+ if (!length) {
+ status = controller_control_respond_error_string(global, control, F_empty, "Received packet is empty.");
+
+ f_socket_disconnect(&client, f_socket_close_fast_e);
+
+ if (F_status_is_error(status)) return status;
+
+ return F_valid_not;
+ }
+
+ if (length < controller_control_default_socket_header_d) {
+ status = controller_control_respond_error_string(global, control, F_too_large, "Received packet is too small.");
+
+ f_socket_disconnect(&client, f_socket_close_fast_e);
+
+ if (F_status_is_error(status)) return status;
+
+ return F_valid_not;
+ }
+
+ if (length > controller_control_default_socket_buffer_max_d) {
+ status = controller_control_respond_error_string(global, control, F_too_large, "Received packet is too large.");
+
+ f_socket_disconnect(&client, f_socket_close_fast_e);
+
+ if (F_status_is_error(status)) return status;
+
+ return F_valid_not;
+ }
+
+ const uint8_t packet_flag = controller_control_packet_header_flag(buffer);
+ const uint32_t packet_length = controller_control_packet_header_length(packet_flag & controller_control_packet_flag_endian_big_d, buffer);
+
+ if (packet_flag & controller_control_packet_flag_binary_d) {
+ status = controller_control_respond_error_string(global, control, F_supported_not, "Binary is not a currently supported packet mode.");
+
+ f_socket_disconnect(&client, f_socket_close_fast_e);
+
+ if (F_status_is_error(status)) return status;
+
+ return F_supported_not;
+ }
+
+ client.size_read = controller_control_default_socket_buffer_d;
+
+ // Pre-allocate the input buffer.
+ status = f_string_dynamic_increase_by(packet_length, &control->input);
+
+ if (F_status_is_error(status)) {
+ controller_control_respond_error_string(global, control, F_memory_not, "Failure allocating memory.");
+
+ f_socket_disconnect(&client, f_socket_close_fast_e);
+
+ controller_print_error(global->thread, global->main->error, F_status_set_fine(status), "f_string_dynamic_increase_by", F_true);
+
+ return status;
+ }
+
+ {
+ size_t total = 0;
+
+ do {
+ status = f_socket_read(&client, 0, &control->input, &total);
+
+ if (F_status_is_error(status)) {
+ controller_control_respond_error_string(global, control, F_status_set_fine(status), "Failure while reading from client socket.");
+
+ f_socket_disconnect(&client, f_socket_close_fast_e);
+
+ return F_status_set_fine(status);
+ }
+
+ } while (total == client.size_read);
+ }
+
+ if (control->input.used != length) {
+ controller_control_respond_error_string(global, control, F_valid_not, "Received packet header length did not match actual received packet length.");
+
+ f_socket_disconnect(&client, f_socket_close_fast_e);
+
+ return F_valid_not;
+ }
+
+ // @todo process the data.
+
+ // @todo send any responses.
+
+ f_socket_disconnect(&client, f_socket_close_fast_e);
+
+ if (control->input.size > controller_control_default_socket_buffer_d) {
+ status = f_string_dynamic_resize(controller_control_default_socket_buffer_d, &control->input);
+
+ if (F_status_is_error(status)) {
+ controller_print_error(global->thread, global->main->error, F_status_set_fine(status), "f_string_dynamic_resize", F_true);
+
+ return status;
+ }
+ }
+
+ if (control->output.size > controller_control_default_socket_buffer_d) {
+ status = f_string_dynamic_resize(controller_control_default_socket_buffer_d, &control->output);
+
+ if (F_status_is_error(status)) {
+ controller_print_error(global->thread, global->main->error, F_status_set_fine(status), "f_string_dynamic_resize", F_true);
+
+ return status;
+ }
+ }
+
+ return F_none;
+ }
+#endif // _di_controller_control_accept_
+
+#ifndef _di_controller_control_configure_client_
+ f_status_t controller_control_configure_client(const controller_global_t *global, f_socket_t * const client) {
+
+ struct timeval time_out;
+ time_out.tv_sec = 0;
+ time_out.tv_usec = controller_control_default_socket_timeout_d;
+
+ f_status_t status = f_socket_option_set(client, 1, f_socket_option_time_out_receive_d, (void *) &time_out, sizeof(struct timeval));
+
+ if (F_status_is_error_not(status)) {
+ status = f_socket_option_set(client, 1, f_socket_option_time_out_send_d, (void *) &time_out, sizeof(struct timeval));
+ }
+
+ if (F_status_is_error(status)) {
+ controller_print_error(global->thread, global->main->error, F_status_set_fine(status), "f_socket_option_set", F_true);
+ }
+
+ return status;
+ }
+#endif // _di_controller_control_configure_client_
+
+#ifndef _di_controller_control_configure_server_
+ f_status_t controller_control_configure_server(const controller_global_t *global, f_socket_t * const server) {
+
+ const struct linger value = { 1, controller_control_default_socket_linger_d };
+
+ const f_status_t status = f_socket_option_set(server, 1, f_socket_option_linger_d, (void *) &value, sizeof(struct linger));
+
+ if (F_status_is_error(status)) {
+ controller_print_error(global->thread, global->main->error, F_status_set_fine(status), "f_socket_option_set", F_true);
+ }
+
+ return status;
+ }
+#endif // _di_controller_control_configure_server_
+
+#ifndef _di_controller_control_packet_header_flag_
+ uint8_t controller_control_packet_header_flag(const char buffer[]) {
+ return (uint8_t) ((buffer[0] & 0x8) ? controller_control_packet_flag_binary_d : 0) | ((buffer[0] & 0x4) ? controller_control_packet_flag_endian_big_d : 0);
+ }
+#endif // _di_controller_control_packet_header_flag_
+
+#ifndef _di_controller_control_packet_header_length_
+ uint32_t controller_control_packet_header_length(const bool is_big, const char buffer[]) {
+
+ register uint32_t length = (((buffer[0] & 0x3f) << 26) | (buffer[1] << 18) | (buffer[2] << 10) | (buffer[3] << 2) | ((buffer[4] & 0xc0) >> 6));
+
+ #ifdef _is_F_endian_big
+ if (is_big) {
+ return length;
+ }
+ #else
+ if (!is_big) {
+ return length;
+ }
+ #endif // _is_F_endian_big
+
+ length = (length & 0x55555555 << 1) | (length & 0x55555555 >> 1);
+ length = (length & 0x33333333 << 2) | (length & 0xcccccccc >> 2);
+ length = (length & 0x0f0f0f0f << 4) | (length & 0xf0f0f0f0 >> 4);
+ length = (length & 0x00ff00ff << 8) | (length & 0xff00ff00 >> 8);
+
+ return (length & 0x0000ffff << 16) | (length & 0xffff0000 >> 16);
+ }
+#endif // _di_controller_control_packet_header_length_
+
+#ifndef _di_controller_control_respond_build_header_
+ f_status_t controller_control_respond_build_header(const controller_global_t *global, controller_control_t * const control, const f_string_static_t type, const f_string_static_t status, const f_array_length_t length) {
+
+ f_status_t status2 = F_none;
+
+ const f_string_static_t object_header = macro_f_string_static_t_initialize(f_fss_string_header_s, F_fss_string_header_s_length);
+ const f_string_static_t object_type = macro_f_string_static_t_initialize(controller_type_s, controller_type_s_length);
+ const f_string_static_t object_status = macro_f_string_static_t_initialize(controller_status_s, controller_status_s_length);
+ const f_string_static_t object_length = macro_f_string_static_t_initialize(controller_length_s, controller_length_s_length);
+ const f_state_t state = f_state_t_initialize;
+ const f_conversion_data_t data_conversion = macro_f_conversion_data_t_initialize(10, 0, 1);
+
+ f_string_statics_t content = f_string_statics_t_initialize;
+ f_string_static_t contents[1];
+ content.array = contents;
+ content.used = 1;
+ content.size = 1;
+
+ control->cache_1.used = 0;
+ control->cache_2.used = 0;
+
+ // Header: type.
+ if (type.used) {
+ contents[0] = type;
+
+ status2 = fll_fss_extended_write_string(object_type, content, 0, state, &control->cache_1);
+ if (F_status_is_error(status2)) return status2;
+ }
+
+ // Header: status.
+ if (status.used) {
+ contents[0] = status;
+
+ status2 = fll_fss_extended_write_string(object_status, content, 0, state, &control->cache_1);
+ if (F_status_is_error(status2)) return status2;
+
+ control->cache_2.used = 0;
+ }
+
+ // Header: length.
+ status2 = f_conversion_number_unsigned_to_string(length, data_conversion, &control->cache_2);
+ if (F_status_is_error(status2)) return status2;
+
+ contents[0] = control->cache_2;
+
+ status2 = fll_fss_extended_write_string(object_length, content, 0, state, &control->cache_1);
+ if (F_status_is_error(status2)) return status2;
+
+ // Prepend the identifier comment to the output.
+ status2 = f_string_append(controller_payload_type_s, controller_payload_type_s_length, &control->output);
+ if (F_status_is_error(status2)) return status2;
+
+ // Append entire header block to the output.
+ status2 = fll_fss_payload_write_string(object_header, control->cache_1, F_false, 0, state, &control->output);
+ if (F_status_is_error(status2)) return status2;
+
+ return F_none;
+ }
+#endif // _di_controller_control_respond_build_header_
+
+#ifndef _di_controller_control_respond_error_
+ f_status_t controller_control_respond_error(const controller_global_t *global, controller_control_t * const control, const f_status_t status, const f_string_static_t message) {
+
+ f_status_t status2 = F_none;
+ const f_state_t state = f_state_t_initialize;
+
+ control->output.used = 0;
+ control->cache_3.used = 0;
+
+ {
+ const f_conversion_data_t data_conversion = macro_f_conversion_data_t_initialize(10, 0, 1);
+
+ status2 = f_conversion_number_unsigned_to_string(F_status_set_fine(status), data_conversion, &control->cache_3);
+ if (F_status_is_error(status2)) return status2;
+ }
+
+ {
+ const f_string_static_t content_error = macro_f_string_static_t_initialize(controller_error_s, controller_error_s_length);
+
+ status2 = controller_control_respond_build_header(global, control, content_error, control->cache_3, message.used);
+ if (F_status_is_error(status2)) return status2;
+ }
+
+ {
+ const f_string_static_t object_payload = macro_f_string_static_t_initialize(f_fss_string_payload_s, F_fss_string_payload_s_length);
+
+ status2 = fll_fss_payload_write_string(object_payload, message, F_false, 0, state, &control->output);
+ if (F_status_is_error(status2)) return status2;
+ }
+
+ return f_socket_write(control->client, 0, control->output.string, 0);
+ }
+#endif // _di_controller_control_respond_error_
+
+#ifndef _di_controller_control_respond_error_string_
+ f_status_t controller_control_respond_error_string(const controller_global_t *global, controller_control_t * const control, const f_status_t status, const f_string_t message) {
+
+ const f_string_static_t string = macro_f_string_static_t_initialize(message, strlen(message));
+
+ return controller_control_respond_error(global, control, status, string);
+ }
+#endif // _di_controller_control_respond_error_string_
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
--- /dev/null
+/**
+ * FLL - Level 3
+ *
+ * Project: Controller
+ * API Version: 0.5
+ * Licenses: lgpl-2.1-or-later
+ */
+#ifndef _PRIVATE_control_h
+#define _PRIVATE_control_h
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Accept connections from a control socket server.
+ *
+ * Connectons are processed and actions are performed.
+ *
+ * @param global
+ * The global data.
+ * @param control
+ * The control data structure.
+ *
+ * @return
+ * F_none on success.
+ * F_valid_not on invalid packet from client (this is not an error with the function itself and so no error bit is set).
+ *
+ * Errors (with error bit) from: f_socket_accept().
+ *
+ * @see f_socket_accept()
+ */
+#ifndef _di_controller_control_accept_
+ extern f_status_t controller_control_accept(const controller_global_t *global, controller_control_t * const control) F_attribute_visibility_internal_d;
+#endif // _di_controller_control_accept_
+
+/**
+ * Configure client socket settings.
+ *
+ * @param global
+ * The global data.
+ * @param client
+ * The client socket structure.
+ *
+ * @return
+ * F_none on success.
+ *
+ * Errors (with error bit) from: f_socket_option_set().
+ *
+ * @see f_socket_option_set()
+ */
+#ifndef _di_controller_control_configure_client_
+ extern f_status_t controller_control_configure_client(const controller_global_t *global, f_socket_t * const client) F_attribute_visibility_internal_d;
+#endif // _di_controller_control_configure_client_
+
+/**
+ * Configure server socket settings.
+ *
+ * @param global
+ * The global data.
+ * @param server
+ * The server socket structure.
+ *
+ * @return
+ * F_none on success.
+ *
+ * Errors (with error bit) from: f_socket_option_set().
+ *
+ * @see f_socket_option_set()
+ */
+#ifndef _di_controller_control_configure_server_
+ extern f_status_t controller_control_configure_server(const controller_global_t *global, f_socket_t * const server) F_attribute_visibility_internal_d;
+#endif // _di_controller_control_configure_server_
+
+/**
+ * Given the header buffer, get the flag bits.
+ *
+ * @param buffer
+ * The buffer to read the length of and get the
+ *
+ * @return
+ * The 8-bit number representing the flags.
+ */
+#ifndef _di_controller_control_packet_header_flag_
+ extern uint8_t controller_control_packet_header_flag(const char buffer[]) F_attribute_visibility_internal_d;
+#endif // _di_controller_control_packet_header_flag_
+
+/**
+ * Given the header buffer, get the length bits.
+ *
+ * The endianness is automatically detected and swapped by this function to guarantee host order bytes.
+ *
+ * @param is_big
+ * If TRUE, then the length in the buffer is in big endian format.
+ * If FALSE, then the length in the buffer is in little endian format.
+ * @param buffer
+ * The buffer to read the length of and get the
+ *
+ * @return
+ * The 32-bit number representing the length.
+ */
+#ifndef _di_controller_control_packet_header_length_
+ extern uint32_t controller_control_packet_header_length(const bool is_big, const char buffer[]) F_attribute_visibility_internal_d;
+#endif // _di_controller_control_packet_header_length_
+
+/**
+ * Accept connections from a control socket server.
+ *
+ * Connectons are processed and actions are performed.
+ *
+ * @param global
+ * The global data.
+ * @param server
+ * The server socket structure.
+ * @param packet
+ * The control packet data structure.
+ *
+ * @return
+ * F_none on success.
+ *
+ * Errors (with error bit) from: f_socket_accept().
+ * Errors (with error bit) from: f_socket_read().
+ *
+ * @see f_socket_accept()
+ * @see f_socket_read()
+ */
+#ifndef _di_controller_control_respond_
+ extern f_status_t controller_control_respond(const controller_global_t *global, controller_control_t * const control) F_attribute_visibility_internal_d;
+#endif // _di_controller_control_respond_
+
+/**
+ * Construct the header portion of the payload.
+ *
+ * This also prepends the FSS identifier comment.
+ *
+ * This resets and uses control.cache_1 and control.cache_2.
+ * Do not use either of these for passing strings to this function.
+ *
+ * The results of this function are appended to control.output.
+ *
+ * @param global
+ * The global data.
+ * @param control
+ * The control structure.
+ * @param type
+ * The packet type.
+ * Set type.used to 0 to not add to the header.
+ * @param status
+ * The status code.
+ * Set status.used to 0 to not add to the header.
+ * @param length
+ * The length of the payload Content.
+ * This is always added to the header.
+ *
+ * @return
+ * F_none on success.
+ *
+ * Errors (with error bit) from: f_conversion_number_unsigned_to_string().
+ * Errors (with error bit) from: f_string_append().
+ * Errors (with error bit) from: fll_fss_extended_write_string().
+ * Errors (with error bit) from: fll_fss_payload_write_string().
+ *
+ * @see f_conversion_number_unsigned_to_string()
+ * @see f_string_append()
+ * @see fll_fss_extended_write_string()
+ * @see fll_fss_payload_write_string()
+ */
+#ifndef _di_controller_control_respond_build_header_
+ extern f_status_t controller_control_respond_build_header(const controller_global_t *global, controller_control_t * const control, const f_string_static_t type, const f_string_static_t status, const f_array_length_t length) F_attribute_visibility_internal_d;
+#endif // _di_controller_control_respond_build_header_
+
+/**
+ * Accept connections from a control socket server.
+ *
+ * Connectons are processed and actions are performed.
+ *
+ * This resets and uses control.cache_3 and control.output.
+ * This calls functions that resets and uses control.cache_1, control.cache_2.
+ *
+ * Common response error codes:
+ * - F_empty: Packet is empty (does not even have headers).
+ * - F_packet_not: Packet is not a valid packet.
+ * - F_supported_not: Format or action is not supported.
+ * - F_too_large: Packet is too large (exceeds max packet length).
+ * - F_too_small: Packet is too small (smaller than minimum header length).
+ *
+ * @param global
+ * The global data.
+ * @param control
+ * The control structure.
+ * @param status
+ * The status code.
+ * @param message
+ * A status message.
+ *
+ * @return
+ * Result of f_socket_write() on success.
+ *
+ * Errors (with error bit) from: f_socket_write().
+ *
+ * @see f_socket_write()
+ */
+#ifndef _di_controller_control_respond_error_
+ extern f_status_t controller_control_respond_error(const controller_global_t *global, controller_control_t * const control, const f_status_t status, const f_string_static_t message) F_attribute_visibility_internal_d;
+#endif // _di_controller_control_respond_error_
+
+/**
+ * Alternate version of controller_control_respond_error() that accepts f_string_t for message.
+ *
+ * This calls functions that resets and uses control.cache_1, control.cache_2, control.cache_3, and control.output.
+ *
+ * @param global
+ * The global data.
+ * @param control
+ * The control structure.
+ * @param status
+ * The status code.
+ * @param message
+ * A status message.
+ * This must be a NULL terminated string so that functions like strlen() may get the length of this string.
+ *
+ * @return
+ * Result of controller_control_respond_error() on success.
+ *
+ * Errors (with error bit) from: controller_control_respond_error().
+ *
+ * @see controller_control_respond_error()
+ */
+#ifndef _di_controller_control_respond_error_string_
+ extern f_status_t controller_control_respond_error_string(const controller_global_t *global, controller_control_t * const control, const f_status_t status, const f_string_t message) F_attribute_visibility_internal_d;
+#endif // _di_controller_control_respond_error_string_
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // _PRIVATE_control_h
--- /dev/null
+#include "../controller/controller.h"
+#include "../common/private-common.h"
+#include "private-control.h"
+#include "../lock/private-lock_print.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
--- /dev/null
+/**
+ * FLL - Level 3
+ *
+ * Project: Controller
+ * API Version: 0.5
+ * Licenses: lgpl-2.1-or-later
+ */
+#ifndef _PRIVATE_control_print_h
+#define _PRIVATE_control_print_h
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // _PRIVATE_control_print_h
--- /dev/null
+#include "controller.h"
+#include "../common/private-common.h"
+#include "../control/private-control.h"
+#include "private-controller.h"
+#include "private-controller_print.h"
+#include "../entry/private-entry.h"
+#include "../lock/private-lock_print.h"
+#include "../rule/private-rule.h"
+#include "../thread/private-thread.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef _di_controller_print_help_
+ f_status_t controller_print_help(controller_main_t * const main) {
+
+ controller_lock_print(main->output.to, 0);
+
+ fll_program_print_help_header(main->output.to, main->context, main->program_name_long, controller_program_version_s);
+
+ fll_program_print_help_option(main->output.to, main->context, f_console_standard_short_help_s, f_console_standard_long_help_s, f_console_symbol_short_enable_s, f_console_symbol_long_enable_s, " Print this help message.");
+ fll_program_print_help_option(main->output.to, main->context, f_console_standard_short_dark_s, f_console_standard_long_dark_s, f_console_symbol_short_disable_s, f_console_symbol_long_disable_s, " Output using colors that show up better on dark backgrounds.");
+ fll_program_print_help_option(main->output.to, main->context, f_console_standard_short_light_s, f_console_standard_long_light_s, f_console_symbol_short_disable_s, f_console_symbol_long_disable_s, " Output using colors that show up better on light backgrounds.");
+ fll_program_print_help_option(main->output.to, main->context, f_console_standard_short_no_color_s, f_console_standard_long_no_color_s, f_console_symbol_short_disable_s, f_console_symbol_long_disable_s, "Do not main->output.to in color.");
+ fll_program_print_help_option(main->output.to, main->context, f_console_standard_short_quiet_s, f_console_standard_long_quiet_s, f_console_symbol_short_disable_s, f_console_symbol_long_disable_s, " Decrease verbosity beyond normal main->output.to.");
+ fll_program_print_help_option(main->output.to, main->context, f_console_standard_short_normal_s, f_console_standard_long_normal_s, f_console_symbol_short_disable_s, f_console_symbol_long_disable_s, " Set verbosity to normal main->output.to.");
+ fll_program_print_help_option(main->output.to, main->context, f_console_standard_short_verbose_s, f_console_standard_long_verbose_s, f_console_symbol_short_disable_s, f_console_symbol_long_disable_s, " Increase verbosity beyond normal main->output.to.");
+ fll_program_print_help_option(main->output.to, main->context, f_console_standard_short_debug_s, f_console_standard_long_debug_s, f_console_symbol_short_disable_s, f_console_symbol_long_disable_s, " Enable debugging, inceasing verbosity beyond normal main->output.to.");
+ fll_program_print_help_option(main->output.to, main->context, f_console_standard_short_version_s, f_console_standard_long_version_s, f_console_symbol_short_disable_s, f_console_symbol_long_disable_s, " Print only the version number.");
+
+ f_print_character(f_string_eol_s[0], main->output.to.stream);
+
+ fll_program_print_help_option(main->output.to, main->context, controller_short_cgroup_s, controller_long_cgroup_s, f_console_symbol_short_enable_s, f_console_symbol_long_enable_s, " Specify a custom control group file path, such as '" F_control_group_path_system_prefix_s F_control_group_path_system_default_s "'.");
+ fll_program_print_help_option(main->output.to, main->context, controller_short_daemon_s, controller_long_daemon_s, f_console_symbol_short_enable_s, f_console_symbol_long_enable_s, " Run in daemon only mode (do not process the entry).");
+ fll_program_print_help_option(main->output.to, main->context, controller_short_init_s, controller_long_init_s, f_console_symbol_short_enable_s, f_console_symbol_long_enable_s, " The program will run as an init replacement.");
+ fll_program_print_help_option(main->output.to, main->context, controller_short_interruptible_s, controller_long_interruptible_s, f_console_symbol_short_enable_s, f_console_symbol_long_enable_s, " Designate that this program can be interrupted by a signal.");
+ fll_program_print_help_option(main->output.to, main->context, controller_short_pid_s, controller_long_pid_s, f_console_symbol_short_enable_s, f_console_symbol_long_enable_s, " Specify a custom pid file path, such as '" controller_path_pid_s CONTROLLER_default_s controller_path_suffix_s "'.");
+ fll_program_print_help_option(main->output.to, main->context, controller_short_settings_s, controller_long_settings_s, f_console_symbol_short_enable_s, f_console_symbol_long_enable_s, " Specify a custom settings path, such as '" controller_path_settings_s "'.");
+ fll_program_print_help_option(main->output.to, main->context, controller_short_simulate_s, controller_long_simulate_s, f_console_symbol_short_enable_s, f_console_symbol_long_enable_s, " Run as a simulation.");
+ fll_program_print_help_option(main->output.to, main->context, controller_short_uninterruptible_s, controller_long_uninterruptible_s, f_console_symbol_short_enable_s, f_console_symbol_long_enable_s, "Designate that this program cannot be interrupted by a signal.");
+ fll_program_print_help_option(main->output.to, main->context, controller_short_validate_s, controller_long_validate_s, f_console_symbol_short_enable_s, f_console_symbol_long_enable_s, " Validate the settings (entry and rules) without running (does not simulate).");
+
+ fll_program_print_help_usage(main->output.to, main->context, main->program_name, "entry");
+
+ fl_print_format(" When both the %[%s%s%] parameter and the", main->output.to.stream, main->context.set.notable, f_console_symbol_long_enable_s, controller_long_simulate_s, main->context.set.notable);
+ fl_print_format(" %[%s%s%] parameter are specified, then additional information on each would be executed rule is printed but no simulation is performed.%c%c", main->output.to.stream, main->context.set.notable, f_console_symbol_long_enable_s, controller_long_validate_s, main->context.set.notable, f_string_eol_s[0], f_string_eol_s[0]);
+
+ fl_print_format(" The default interrupt behavior is to operate as if the %[%s%s%] parameter is passed.%c%c", main->output.to.stream, main->context.set.notable, f_console_symbol_long_enable_s, main->setting_default.used ? controller_long_uninterruptible_s : controller_long_interruptible_s, main->context.set.notable, f_string_eol_s[0], f_string_eol_s[0]);
+
+ fl_print_format(" Specify an empty string for the %[%s%s%] parameter to disable pid file creation for this program.%c%c", main->output.to.stream, main->context.set.notable, f_console_symbol_long_enable_s, controller_long_pid_s, main->context.set.notable, f_string_eol_s[0], f_string_eol_s[0]);
+
+ controller_unlock_print_flush(main->output.to, 0);
+
+ return F_none;
+ }
+#endif // _di_controller_print_help_
+
+#ifndef _di_controller_main_
+ f_status_t controller_main(controller_main_t * const main, const f_console_arguments_t *arguments) {
+
+ f_status_t status = F_none;
+
+ {
+ const f_console_parameters_t parameters = macro_f_console_parameters_t_initialize(main->parameters, controller_total_parameters_d);
+
+ {
+ f_console_parameter_id_t ids[3] = { controller_parameter_no_color_e, controller_parameter_light_e, controller_parameter_dark_e };
+ const f_console_parameter_ids_t choices = macro_f_console_parameter_ids_t_initialize(ids, 3);
+
+ status = fll_program_parameter_process(*arguments, parameters, choices, F_true, &main->remaining, &main->context);
+
+ main->output.set = &main->context.set;
+ main->error.set = &main->context.set;
+ main->warning.set = &main->context.set;
+
+ if (main->context.set.error.before) {
+ main->output.context = f_color_set_empty_s;
+ main->output.notable = main->context.set.notable;
+
+ main->error.context = main->context.set.error;
+ main->error.notable = main->context.set.notable;
+
+ main->warning.context = main->context.set.warning;
+ main->warning.notable = main->context.set.notable;
+ }
+ else {
+ f_color_set_t *sets[] = { &main->output.context, &main->output.notable, &main->error.context, &main->error.notable, &main->warning.context, &main->warning.notable, 0 };
+
+ fll_program_parameter_process_empty(&main->context, sets);
+ }
+
+ if (F_status_is_error(status)) {
+ if (main->error.verbosity != f_console_verbosity_quiet_e) {
+ fll_error_print(main->error, F_status_set_fine(status), "fll_program_parameter_process", F_true);
+ fll_print_terminated(f_string_eol_s, main->error.to.stream);
+ }
+
+ controller_main_delete(main);
+
+ return F_status_set_error(status);
+ }
+ }
+
+ // Identify priority of verbosity related parameters.
+ {
+ f_console_parameter_id_t ids[4] = { controller_parameter_verbosity_quiet_e, controller_parameter_verbosity_normal_e, controller_parameter_verbosity_verbose_e, controller_parameter_verbosity_debug_e };
+ f_console_parameter_id_t choice = 0;
+ const f_console_parameter_ids_t choices = macro_f_console_parameter_ids_t_initialize(ids, 4);
+
+ status = f_console_parameter_prioritize_right(parameters, choices, &choice);
+
+ if (F_status_is_error(status)) {
+ controller_main_delete(main);
+
+ return status;
+ }
+
+ if (choice == controller_parameter_verbosity_quiet_e) {
+ main->output.verbosity = f_console_verbosity_quiet_e;
+ main->error.verbosity = f_console_verbosity_quiet_e;
+ main->warning.verbosity = f_console_verbosity_quiet_e;
+ }
+ else if (choice == controller_parameter_verbosity_normal_e) {
+ main->output.verbosity = f_console_verbosity_normal_e;
+ main->error.verbosity = f_console_verbosity_normal_e;
+ main->warning.verbosity = f_console_verbosity_normal_e;
+ }
+ else if (choice == controller_parameter_verbosity_verbose_e) {
+ main->output.verbosity = f_console_verbosity_verbose_e;
+ main->error.verbosity = f_console_verbosity_verbose_e;
+ main->warning.verbosity = f_console_verbosity_verbose_e;
+ }
+ else if (choice == controller_parameter_verbosity_debug_e) {
+ main->output.verbosity = f_console_verbosity_debug_e;
+ main->error.verbosity = f_console_verbosity_debug_e;
+ main->warning.verbosity = f_console_verbosity_debug_e;
+ }
+ }
+
+ status = F_none;
+ }
+
+ if (main->parameters[controller_parameter_help_e].result == f_console_result_found_e) {
+ controller_print_help(main);
+
+ controller_main_delete(main);
+
+ return F_none;
+ }
+
+ if (main->parameters[controller_parameter_version_e].result == f_console_result_found_e) {
+ controller_lock_print(main->output.to, 0);
+
+ fll_program_print_version(main->output.to, controller_program_version_s);
+
+ controller_unlock_print_flush(main->output.to, 0);
+
+ controller_main_delete(main);
+
+ return F_none;
+ }
+
+ controller_setting_t setting = controller_setting_t_initialize;
+
+ struct sockaddr_un address;
+ setting.control_socket.address = (struct sockaddr *) &address;
+ setting.control_socket.domain = f_socket_domain_file_d;
+ setting.control_socket.type = f_socket_type_stream_d;
+ setting.control_socket.length = sizeof(struct sockaddr_un);
+
+ memset(&address, 0, setting.control_socket.length);
+
+ if (main->remaining.used) {
+ status = f_string_append_nulless(arguments->argv[main->remaining.array[0]], strnlen(arguments->argv[main->remaining.array[0]], f_console_parameter_size), &setting.name_entry);
+ }
+ else {
+ status = f_string_append_nulless(controller_default_s, controller_default_s_length, &setting.name_entry);
+ }
+
+ if (F_status_is_error(status)) {
+ fll_error_print(main->error, F_status_set_fine(status), "f_string_append_nulless", F_true);
+
+ controller_main_delete(main);
+
+ return status;
+ }
+
+ status = f_string_dynamic_terminate_after(&setting.name_entry);
+
+ if (F_status_is_error(status)) {
+ fll_error_print(main->error, F_status_set_fine(status), "f_string_dynamic_terminate_after", F_true);
+
+ controller_main_delete(main);
+
+ return status;
+ }
+
+ if (main->parameters[controller_parameter_init_e].result == f_console_result_found_e) {
+ main->as_init = F_true;
+ }
+
+ if (main->as_init) {
+ setting.mode = controller_setting_mode_service_e;
+ }
+
+ if (main->parameters[controller_parameter_settings_e].result == f_console_result_found_e) {
+ if (main->error.verbosity != f_console_verbosity_quiet_e) {
+ controller_lock_print(main->error.to, 0);
+
+ fl_print_format("%c%[%SThe parameter '%]", main->error.to.stream, f_string_eol_s[0], main->error.context, main->error.prefix ? main->error.prefix : f_string_empty_s, main->error.context);
+ fl_print_format("%[%s%s%]", main->error.to.stream, main->context.set.notable, f_console_symbol_long_enable_s, controller_long_settings_s, main->context.set.notable);
+ fl_print_format("%[' is specified, but no value is given.%]%c", main->error.to.stream, main->error.context, main->error.context, f_string_eol_s[0]);
+
+ controller_unlock_print_flush(main->error.to, 0);
+ }
+
+ status = F_status_set_error(F_parameter);
+ }
+ else if (main->parameters[controller_parameter_settings_e].locations.used) {
+ const f_array_length_t location = main->parameters[controller_parameter_settings_e].values.array[main->parameters[controller_parameter_settings_e].values.used - 1];
+
+ status = fll_path_canonical(arguments->argv[location], &setting.path_setting);
+
+ if (F_status_is_error(status)) {
+ fll_error_file_print(main->error, F_status_set_fine(status), "fll_path_canonical", F_true, arguments->argv[location], "verify", fll_error_file_type_path_e);
+ }
+ }
+ else {
+ if (main->parameters[controller_parameter_init_e].result == f_console_result_found_e && !main->as_init) {
+ status = f_string_append(controller_path_settings_init_s, controller_path_settings_init_s_length, &setting.path_setting);
+ }
+ else if (main->setting_default.used) {
+ status = f_string_append(main->setting_default.string, main->setting_default.used, &setting.path_setting);
+ }
+ else {
+ status = f_string_append(controller_path_settings_s, controller_path_settings_s_length, &setting.path_setting);
+ }
+
+ if (F_status_is_error(status)) {
+ fll_error_print(main->error, F_status_set_fine(status), "f_string_append", F_true);
+ }
+ }
+
+ if (F_status_is_error_not(status)) {
+ if (main->parameters[controller_parameter_pid_e].result == f_console_result_found_e) {
+ if (main->error.verbosity != f_console_verbosity_quiet_e) {
+ controller_lock_print(main->error.to, 0);
+
+ fl_print_format("%c%[%SThe parameter '%]", main->error.to.stream, f_string_eol_s[0], main->error.context, main->error.prefix ? main->error.prefix : f_string_empty_s, main->error.context);
+ fl_print_format("%[%s%s%]", main->error.to.stream, main->context.set.notable, f_console_symbol_long_enable_s, controller_long_pid_s, main->context.set.notable);
+ fl_print_format("%[' is specified, but no value is given.%]%c", main->error.to.stream, main->error.context, main->error.context, f_string_eol_s[0]);
+
+ controller_unlock_print_flush(main->error.to, 0);
+ }
+
+ status = F_status_set_error(F_parameter);
+ }
+ else if (main->parameters[controller_parameter_pid_e].locations.used) {
+ const f_array_length_t location = main->parameters[controller_parameter_pid_e].values.array[main->parameters[controller_parameter_pid_e].values.used - 1];
+
+ if (strnlen(arguments->argv[location], f_console_parameter_size)) {
+ status = fll_path_canonical(arguments->argv[location], &setting.path_pid);
+
+ if (F_status_is_error(status)) {
+ fll_error_file_print(main->error, F_status_set_fine(status), "fll_path_canonical", F_true, arguments->argv[location], "verify", fll_error_file_type_path_e);
+ }
+ }
+ else {
+ setting.path_pid.used = 0;
+ }
+ }
+ }
+
+ if (F_status_is_error_not(status) && !setting.path_pid.used && !main->parameters[controller_parameter_pid_e].locations.used) {
+ if (main->parameters[controller_parameter_init_e].result == f_console_result_found_e) {
+ status = f_string_append(controller_path_pid_init_s, controller_path_pid_init_s_length, &setting.path_pid);
+ }
+ else {
+ status = f_string_append(main->path_pid.string, main->path_pid.used, &setting.path_pid);
+ }
+
+ if (F_status_is_error_not(status)) {
+ status = f_string_append(setting.name_entry.string, setting.name_entry.used, &setting.path_pid);
+ }
+
+ if (F_status_is_error_not(status)) {
+ status = f_string_append(controller_path_suffix_s, controller_path_suffix_s_length, &setting.path_pid);
+ }
+
+ if (F_status_is_error(status)) {
+ fll_error_print(main->error, F_status_set_fine(status), "f_string_append", F_true);
+ }
+ }
+
+ if (F_status_is_error_not(status)) {
+ if (main->parameters[controller_parameter_cgroup_e].result == f_console_result_found_e) {
+ if (main->error.verbosity != f_console_verbosity_quiet_e) {
+ controller_lock_print(main->error.to, 0);
+
+ fl_print_format("%c%[%SThe parameter '%]", main->error.to.stream, f_string_eol_s[0], main->error.context, main->error.prefix ? main->error.prefix : f_string_empty_s, main->error.context);
+ fl_print_format("%[%s%s%]", main->error.to.stream, main->context.set.notable, f_console_symbol_long_enable_s, controller_long_cgroup_s, main->context.set.notable);
+ fl_print_format("%[' is specified, but no value is given.%]%c", main->error.to.stream, main->error.context, main->error.context, f_string_eol_s[0]);
+
+ controller_unlock_print_flush(main->error.to, 0);
+ }
+
+ status = F_status_set_error(F_parameter);
+ }
+ else if (main->parameters[controller_parameter_cgroup_e].locations.used) {
+ const f_array_length_t location = main->parameters[controller_parameter_cgroup_e].values.array[main->parameters[controller_parameter_cgroup_e].values.used - 1];
+
+ if (strnlen(arguments->argv[location], f_console_parameter_size)) {
+ status = fll_path_canonical(arguments->argv[location], &setting.path_cgroup);
+
+ if (F_status_is_error(status)) {
+ fll_error_file_print(main->error, F_status_set_fine(status), "fll_path_canonical", F_true, arguments->argv[location], "verify", fll_error_file_type_path_e);
+ }
+ else {
+ status = f_string_append_assure(F_path_separator_s, 1, &setting.path_cgroup);
+
+ if (F_status_is_error(status)) {
+ fll_error_print(main->error, F_status_set_fine(status), "f_string_append_assure", F_true);
+ }
+ else {
+ status = f_string_dynamic_terminate_after(&setting.path_cgroup);
+
+ if (F_status_is_error(status)) {
+ fll_error_print(main->error, F_status_set_fine(status), "f_string_dynamic_terminate_after", F_true);
+ }
+ }
+ }
+ }
+ else {
+ if (main->warning.verbosity == f_console_verbosity_debug_e) {
+ controller_lock_print(main->warning.to, 0);
+
+ fl_print_format("%c%[%SThe parameter '%]", main->warning.to.stream, f_string_eol_s[0], main->warning.context, main->warning.prefix ? main->warning.prefix : f_string_empty_s, main->warning.context);
+ fl_print_format("%[%s%s%]", main->warning.to.stream, main->context.set.notable, f_console_symbol_long_enable_s, controller_long_cgroup_s, main->context.set.notable);
+ fl_print_format("%[' must be a file directory path but instead is an empty string, falling back to the default.%]%c", main->warning.to.stream, main->warning.context, main->warning.context, f_string_eol_s[0]);
+
+ controller_unlock_print_flush(main->warning.to, 0);
+ }
+ }
+ }
+ }
+
+ if (F_status_is_error_not(status) && main->parameters[controller_parameter_daemon_e].result == f_console_result_found_e) {
+ if (main->parameters[controller_parameter_validate_e].result == f_console_result_found_e) {
+ if (main->error.verbosity != f_console_verbosity_quiet_e) {
+ controller_lock_print(main->error.to, 0);
+
+ fl_print_format("%c%[%SThe parameter '%]", main->error.to.stream, f_string_eol_s[0], main->error.context, main->error.prefix ? main->error.prefix : f_string_empty_s, main->error.context);
+ fl_print_format("%[' must not be specified with the parameter '%]", main->error.to.stream, main->error.context, main->error.context);
+ fl_print_format("%[%s%s%]", main->error.to.stream, main->context.set.notable, f_console_symbol_long_enable_s, controller_long_daemon_s, main->context.set.notable);
+ fl_print_format("%['.%]%c", main->error.to.stream, main->error.context, main->error.context, f_string_eol_s[0]);
+
+ controller_unlock_print_flush(main->error.to, 0);
+ }
+
+ status = F_status_set_error(F_parameter);
+ }
+ }
+
+ // Handle defaults dependent on the "as init" execution state.
+ if (main->as_init) {
+ setting.entry.pid = controller_entry_pid_disable_e;
+ setting.entry.show = controller_entry_show_init_e;
+
+ if (main->parameters[controller_parameter_interruptible_e].result == f_console_result_found_e) {
+ setting.interruptible = F_true;
+ }
+ else {
+ setting.interruptible = F_false;
+ }
+ }
+ else {
+ if (main->parameters[controller_parameter_uninterruptible_e].result == f_console_result_found_e) {
+ setting.interruptible = F_false;
+ }
+ else {
+ setting.interruptible = F_true;
+ }
+ }
+
+ if (F_status_is_error_not(status)) {
+ f_signal_set_fill(&main->signal.set);
+
+ status = f_thread_signal_mask(SIG_BLOCK, &main->signal.set, 0);
+
+ if (F_status_is_error_not(status)) {
+ status = f_signal_open(&main->signal);
+ }
+
+ // If there is an error opening a signal descriptor, then do not handle signals.
+ if (F_status_is_error(status)) {
+ f_signal_mask(SIG_UNBLOCK, &main->signal.set, 0);
+ f_signal_close(&main->signal);
+ }
+
+ // A control file path is required.
+ if (!setting.path_cgroup.used) {
+ status = f_string_append_nulless(F_control_group_path_system_prefix_s, F_control_group_path_system_prefix_s_length, &setting.path_cgroup);
+
+ if (F_status_is_error_not(status)) {
+ status = f_string_append_nulless(F_control_group_path_system_default_s, F_control_group_path_system_default_s_length, &setting.path_cgroup);
+ }
+
+ if (F_status_is_error(status)) {
+ fll_error_print(main->error, F_status_set_fine(status), "f_string_append_nulless", F_true);
+ }
+ else {
+ status = f_string_append_assure(F_path_separator_s, 1, &setting.path_cgroup);
+
+ if (F_status_is_error(status)) {
+ fll_error_print(main->error, F_status_set_fine(status), "f_string_append_assure", F_true);
+ }
+ else {
+ status = f_string_dynamic_terminate_after(&setting.path_cgroup);
+
+ if (F_status_is_error(status)) {
+ fll_error_print(main->error, F_status_set_fine(status), "f_string_dynamic_terminate_after", F_true);
+ }
+ }
+ }
+ }
+ }
+
+ if (F_status_is_error_not(status)) {
+ status = controller_thread_main(main, &setting);
+ }
+
+ // Ensure a newline is always put at the end of the program execution, unless in quiet mode.
+ if (F_status_is_error(status) && main->output.verbosity != f_console_verbosity_quiet_e) {
+ if (F_status_set_fine(status) == F_interrupt) {
+ fflush(main->output.to.stream);
+ }
+
+ fll_print_terminated(f_string_eol_s, main->output.to.stream);
+ }
+
+ if (status != F_child && setting.pid_created) {
+ const f_status_t status_delete = controller_file_pid_delete(main->pid, setting.path_pid);
+
+ if (F_status_is_error(status_delete) && main->warning.verbosity == f_console_verbosity_debug_e) {
+ if (F_status_set_fine(status_delete) == F_number_not) {
+ controller_lock_print(main->warning.to, 0);
+
+ fl_print_format("%c%[%SThe pid file '%]", main->warning.to.stream, f_string_eol_s[0], main->warning.context, main->warning.prefix ? main->warning.prefix : f_string_empty_s, main->warning.context);
+ fl_print_format("%[%Q%]", main->warning.to.stream, main->warning.notable, setting.path_pid, main->warning.notable);
+ fl_print_format("%[' must not be specified with the parameter '%]", main->warning.to.stream, main->warning.context, main->warning.context);
+ fl_print_format("%[%i%]", main->warning.to.stream, main->warning.notable, main->pid, main->warning.notable);
+ fl_print_format("%[' doesn't contain the expected number, not deleting file.%]%c", main->warning.to.stream, main->warning.context, main->warning.context, f_string_eol_s[0]);
+
+ controller_unlock_print_flush(main->warning.to, 0);
+ }
+ else if (F_status_set_fine(status_delete) != F_interrupt) {
+ fll_error_file_print(main->warning, F_status_set_fine(status_delete), "controller_file_pid_delete", F_true, setting.path_pid.string, "delete", fll_error_file_type_file_e);
+ }
+ }
+ }
+
+ if (status != F_child && setting.path_control.used) {
+ f_socket_disconnect(&setting.control_socket, f_socket_close_read_write_e);
+
+ if (!setting.control_readonly) {
+ f_file_remove(setting.path_control.string);
+ }
+ }
+
+ controller_setting_delete_simple(&setting);
+ controller_main_delete(main);
+
+ if (status == F_child) {
+ return status;
+ }
+
+ return status;
+ }
+#endif // _di_controller_main_
+
+#ifndef _di_controller_main_delete_
+ f_status_t controller_main_delete(controller_main_t * const main) {
+
+ for (f_array_length_t i = 0; i < controller_total_parameters_d; ++i) {
+
+ macro_f_array_lengths_t_delete_simple(main->parameters[i].locations);
+ macro_f_array_lengths_t_delete_simple(main->parameters[i].locations_sub);
+ macro_f_array_lengths_t_delete_simple(main->parameters[i].values);
+ } // for
+
+ macro_f_array_lengths_t_delete_simple(main->remaining);
+ macro_f_color_context_t_delete_simple(main->context);
+
+ return F_none;
+ }
+#endif // _di_controller_main_delete_
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
--- /dev/null
+/**
+ * FLL - Level 3
+ *
+ * Project: Controller
+ * API Version: 0.5
+ * Licenses: lgpl-2.1-or-later
+ *
+ * This is the Controller program.
+ *
+ * This program utilizes the Featureless Linux Library.
+ * This program provides system service management, much like sysvcontroller and controllerng.
+ * This program can be controlled from user-space via the "control" program.
+ * This program can be used in an initrd and should be capable of pivot root operations.
+ *
+ * @todo Implement "exit" files that are the opposite of "entry" files whereas rules specified within are all called via the "stop" action type.
+ * This would then allow for switching modes.
+ * The "exit" would be specified in the "entry", by name and would be found under "exits" directory alongside the "entries" directory.
+ *
+ * @todo check the return status of unlocks.
+ *
+ * @todo the read/write locks (and unlocks) needs to be more robust in that they need to attempt to keep going even on failure or need to wait until resolvable.
+ * this is done to help ensure that the controller program always continues onward.
+ *
+ * @todo just like with the read/write locks, the out of memory cases need to be handled to keep going instead of bailing.
+ * likely these will need to be sleeps on the premise that eventually memory will clear itself up.
+ */
+#ifndef _controller_h
+
+// include pre-requirements
+#define _GNU_SOURCE
+
+// libc includes
+#include <sched.h>
+#include <string.h>
+#include <time.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <linux/sched.h>
+#include <unistd.h>
+
+// fll-0 includes
+#include <fll/level_0/type.h>
+#include <fll/level_0/status.h>
+#include <fll/level_0/memory.h>
+#include <fll/level_0/type_array.h>
+#include <fll/level_0/string.h>
+#include <fll/level_0/utf.h>
+#include <fll/level_0/account.h>
+#include <fll/level_0/capability.h>
+#include <fll/level_0/color.h>
+#include <fll/level_0/console.h>
+#include <fll/level_0/control_group.h>
+#include <fll/level_0/directory.h>
+#include <fll/level_0/environment.h>
+#include <fll/level_0/execute.h>
+#include <fll/level_0/file.h>
+#include <fll/level_0/fss.h>
+#include <fll/level_0/path.h>
+#include <fll/level_0/pipe.h>
+#include <fll/level_0/print.h>
+#include <fll/level_0/signal.h>
+#include <fll/level_0/socket.h>
+
+// fll-1 includes
+#include <fll/level_1/console.h>
+#include <fll/level_1/control_group.h>
+#include <fll/level_1/environment.h>
+#include <fll/level_1/fss.h>
+#include <fll/level_1/print.h>
+#include <fll/level_1/string.h>
+
+// fll-2 includes
+#include <fll/level_2/control_group.h>
+#include <fll/level_2/error.h>
+#include <fll/level_2/execute.h>
+#include <fll/level_2/fss.h>
+#include <fll/level_2/fss_basic_list.h>
+#include <fll/level_2/fss_extended.h>
+#include <fll/level_2/fss_extended_list.h>
+#include <fll/level_2/fss_payload.h>
+#include <fll/level_2/path.h>
+#include <fll/level_2/print.h>
+#include <fll/level_2/program.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef _di_controller_program_version_
+ #define controller_program_version_major_s F_string_ascii_0_s
+ #define controller_program_version_minor_s F_string_ascii_5_s
+ #define controller_program_version_micro_s F_string_ascii_8_s
+
+ #ifndef controller_program_version_nano_prefix_s
+ #define controller_program_version_nano_prefix_s
+ #endif
+
+ #ifndef controller_program_version_nano_s
+ #define controller_program_version_nano_s
+ #endif
+
+ #define controller_program_version_s controller_program_version_major_s F_string_ascii_period_s controller_program_version_minor_s F_string_ascii_period_s controller_program_version_micro_s controller_program_version_nano_prefix_s controller_program_version_nano_s
+#endif // _di_controller_program_version_
+
+#ifndef _di_controller_program_name_
+ #define controller_program_name_s "controller"
+ #define controller_program_name_long_s "Controller Program"
+
+ #define controller_program_name_init_s "init"
+ #define controller_program_name_init_long_s "Init Program"
+#endif // _di_controller_program_name_
+
+#ifndef _di_controller_defines_
+
+ // The init pid path is a system-specific path and needs to be more easily contolled at compile time.
+ #if defined(_override_controller_path_pid_init_) && defined(_override_controller_path_pid_init_length_)
+ #define controller_path_pid_init_s _override_controller_path_pid_init_
+ #define controller_path_pid_init_s_length _override_controller_path_pid_init_length_
+ #elif defined(_controller_as_init_)
+ #define controller_path_pid_init_s "/var/run/init/init-"
+ #define controller_path_pid_init_s_length 19
+ #else
+ #define controller_path_pid_init_s "/var/run/controller/controller-"
+ #define controller_path_pid_init_s_length 31
+ #endif /* defined(_override_controller_path_pid_init_) && defined(_override_controller_path_pid_init_length_) */
+
+ // The settings path is a system-specific path and needs to be more easily contolled at compile time.
+ #if defined(_override_controller_path_settings_init_) && defined(_override_controller_path_settings_init_length_)
+ #define controller_path_settings_init_s _override_controller_path_settings_init_
+ #define controller_path_settings_init_s_length _override_controller_path_settings_init_length_
+ #elif defined(_controller_as_init_)
+ #define controller_path_settings_init_s "/etc/init"
+ #define controller_path_settings_init_s_length 9
+ #else
+ #define controller_path_settings_init_s "/etc/controller"
+ #define controller_path_settings_init_s_length 15
+ #endif /* defined(_override_controller_path_settings_init_) && defined(_override_controller_path_settings_init_length_) */
+
+ #ifdef _override_controller_default_program_script_
+ #define controller_default_program_script_s _override_controller_default_program_script_
+ #else
+ #define controller_default_program_script_s "bash"
+ #endif // _override_controller_default_program_script_
+
+ #define controller_path_pid_s "controller/run/controller-"
+ #define controller_path_settings_s "controller"
+ #define controller_path_suffix_s ".pid"
+
+ #define controller_path_pid_s_length 26
+ #define controller_path_settings_s_length 10
+ #define controller_path_suffix_s_length 4
+
+ #define controller_short_cgroup_s "c"
+ #define controller_short_daemon_s "d"
+ #define controller_short_init_s "I"
+ #define controller_short_interruptible_s "i"
+ #define controller_short_pid_s "p"
+ #define controller_short_settings_s "s"
+ #define controller_short_simulate_s "S"
+ #define controller_short_uninterruptible_s "U"
+ #define controller_short_validate_s "v"
+
+ #define controller_long_cgroup_s "cgroup"
+ #define controller_long_daemon_s "daemon"
+ #define controller_long_init_s "init"
+ #define controller_long_interruptible_s "interruptible"
+ #define controller_long_pid_s "pid"
+ #define controller_long_settings_s "settings"
+ #define controller_long_simulate_s "simulate"
+ #define controller_long_uninterruptible_s "uninterruptible"
+ #define controller_long_validate_s "validate"
+
+ enum {
+ controller_parameter_help_e,
+ controller_parameter_light_e,
+ controller_parameter_dark_e,
+ controller_parameter_no_color_e,
+ controller_parameter_verbosity_quiet_e,
+ controller_parameter_verbosity_normal_e,
+ controller_parameter_verbosity_verbose_e,
+ controller_parameter_verbosity_debug_e,
+ controller_parameter_version_e,
+
+ controller_parameter_cgroup_e,
+ controller_parameter_daemon_e,
+ controller_parameter_init_e,
+ controller_parameter_interruptible_e,
+ controller_parameter_pid_e,
+ controller_parameter_settings_e,
+ controller_parameter_simulate_e,
+ controller_parameter_uninterruptible_e,
+ controller_parameter_validate_e,
+ };
+
+ #define controller_console_parameter_t_initialize \
+ { \
+ f_console_parameter_t_initialize(f_console_standard_short_help_s, f_console_standard_long_help_s, 0, 0, f_console_type_normal_e), \
+ f_console_parameter_t_initialize(f_console_standard_short_light_s, f_console_standard_long_light_s, 0, 0, f_console_type_inverse_e), \
+ f_console_parameter_t_initialize(f_console_standard_short_dark_s, f_console_standard_long_dark_s, 0, 0, f_console_type_inverse_e), \
+ f_console_parameter_t_initialize(f_console_standard_short_no_color_s, f_console_standard_long_no_color_s, 0, 0, f_console_type_inverse_e), \
+ f_console_parameter_t_initialize(f_console_standard_short_quiet_s, f_console_standard_long_quiet_s, 0, 0, f_console_type_inverse_e), \
+ f_console_parameter_t_initialize(f_console_standard_short_normal_s, f_console_standard_long_normal_s, 0, 0, f_console_type_inverse_e), \
+ f_console_parameter_t_initialize(f_console_standard_short_verbose_s, f_console_standard_long_verbose_s, 0, 0, f_console_type_inverse_e), \
+ f_console_parameter_t_initialize(f_console_standard_short_debug_s, f_console_standard_long_debug_s, 0, 0, f_console_type_inverse_e), \
+ f_console_parameter_t_initialize(f_console_standard_short_version_s, f_console_standard_long_version_s, 0, 0, f_console_type_inverse_e), \
+ f_console_parameter_t_initialize(controller_short_cgroup_s, controller_long_cgroup_s, 0, 1, f_console_type_normal_e), \
+ f_console_parameter_t_initialize(controller_short_daemon_s, controller_long_daemon_s, 0, 0, f_console_type_normal_e), \
+ f_console_parameter_t_initialize(controller_short_init_s, controller_long_init_s, 0, 0, f_console_type_normal_e), \
+ f_console_parameter_t_initialize(controller_short_interruptible_s, controller_long_interruptible_s, 0, 0, f_console_type_normal_e), \
+ f_console_parameter_t_initialize(controller_short_pid_s, controller_long_pid_s, 0, 1, f_console_type_normal_e), \
+ f_console_parameter_t_initialize(controller_short_settings_s, controller_long_settings_s, 0, 1, f_console_type_normal_e), \
+ f_console_parameter_t_initialize(controller_short_simulate_s, controller_long_simulate_s, 0, 0, f_console_type_normal_e), \
+ f_console_parameter_t_initialize(controller_short_uninterruptible_s, controller_long_uninterruptible_s, 0, 0, f_console_type_normal_e), \
+ f_console_parameter_t_initialize(controller_short_validate_s, controller_long_validate_s, 0, 0, f_console_type_normal_e), \
+ }
+
+ #define controller_total_parameters_d 18
+#endif // _di_controller_defines_
+
+#ifndef _di_controller_main_t_
+ typedef struct {
+ f_console_parameter_t parameters[controller_total_parameters_d];
+
+ f_array_lengths_t remaining;
+ bool process_pipe;
+ bool as_init;
+
+ fl_print_t output;
+ fl_print_t error;
+ fl_print_t warning;
+
+ f_signal_t signal;
+
+ pid_t pid;
+ mode_t umask;
+ int child;
+
+ f_string_t program_name;
+ f_string_t program_name_long;
+ f_string_static_t setting_default;
+ f_string_static_t path_pid;
+
+ f_color_context_t context;
+ } controller_main_t;
+
+ #define controller_main_t_initialize \
+ { \
+ controller_console_parameter_t_initialize, \
+ f_array_lengths_t_initialize, \
+ F_false, \
+ F_false, \
+ fl_print_t_initialize, \
+ macro_fl_print_t_initialize_error(), \
+ macro_fl_print_t_initialize_warning(), \
+ f_signal_t_initialize, \
+ 0, \
+ 0, \
+ 0, \
+ f_string_t_initialize, \
+ f_string_t_initialize, \
+ f_string_static_t_initialize, \
+ f_string_static_t_initialize, \
+ f_color_context_t_initialize, \
+ }
+#endif // _di_controller_main_t_
+
+/**
+ * Print help.
+ *
+ * @param main
+ * The main program data.
+ *
+ * @return
+ * F_none on success.
+ */
+#ifndef _di_controller_print_help_
+ extern f_status_t controller_print_help(controller_main_t * const main);
+#endif // _di_controller_print_help_
+
+/**
+ * Execute main program.
+ *
+ * Be sure to call controller_main_delete() after executing this.
+ *
+ * If main.signal is non-zero, then this blocks and handles the following signals:
+ * - F_signal_abort
+ * - F_signal_broken_pipe
+ * - F_signal_hangup
+ * - F_signal_interrupt
+ * - F_signal_quit
+ * - F_signal_termination
+ *
+ * @param main
+ * The main program data.
+ * @param arguments
+ * The parameters passed to the process.
+ *
+ * @return
+ * F_none on success.
+ * F_child if this is a child process returning.
+ *
+ * F_interrupt (with error bit) on receiving a process signal, such as an interrupt signal.
+ *
+ * Status codes (with error bit) are returned on any problem.
+ *
+ * @see controller_main_delete()
+ */
+#ifndef _di_controller_main_
+ extern f_status_t controller_main(controller_main_t * const main, const f_console_arguments_t *arguments);
+#endif // _di_controller_main_
+
+/**
+ * Deallocate main.
+ *
+ * Be sure to call this after executing controller_main().
+ *
+ * If main.signal is non-zero, then this blocks and handles the following signals:
+ * - F_signal_abort
+ * - F_signal_broken_pipe
+ * - F_signal_hangup
+ * - F_signal_interrupt
+ * - F_signal_quit
+ * - F_signal_termination
+ *
+ * @param main
+ * The main program data.
+ *
+ * @return
+ * F_none on success.
+ *
+ * Status codes (with error bit) are returned on any problem.
+ *
+ * @see controller_main()
+ */
+#ifndef _di_controller_main_delete_
+ extern f_status_t controller_main_delete(controller_main_t * const main);
+#endif // _di_controller_main_delete_
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // _controller_h
--- /dev/null
+#include "controller.h"
+#include "../common/private-common.h"
+#include "private-controller.h"
+#include "private-controller_print.h"
+#include "../entry/private-entry_print.h"
+#include "../lock/private-lock_print.h"
+#include "../thread/private-thread_control.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef _di_controller_range_after_number_sign_
+ f_string_range_t controller_range_after_number_sign(const f_string_static_t buffer, const f_string_range_t range) {
+
+ f_string_range_t result = range;
+
+ for (; result.start <= result.stop; ++result.start) {
+
+ if (!buffer.string[result.start]) continue;
+
+ if (buffer.string[result.start] == '-' || buffer.string[result.start] == '+') {
+ ++result.start;
+ }
+
+ break;
+ } // while
+
+ return result;
+ }
+#endif // _di_controller_range_after_number_sign_
+
+#ifndef _di_controller_string_dynamic_rip_nulless_terminated_
+ f_status_t controller_dynamic_rip_nulless_terminated(const f_string_static_t source, const f_string_range_t range, f_string_dynamic_t *destination) {
+
+ f_status_t status = fl_string_dynamic_rip_nulless(source, range, destination);
+ if (F_status_is_error(status)) return status;
+
+ return f_string_dynamic_terminate_after(destination);
+ }
+#endif // _di_controller_string_dynamic_rip_nulless_terminated_
+
+#ifndef _di_controller_string_dynamic_append_terminated_
+ f_status_t controller_dynamic_append_terminated(const f_string_static_t source, f_string_dynamic_t *destination) {
+
+ f_status_t status = f_string_dynamic_append_nulless(source, destination);
+ if (F_status_is_error(status)) return status;
+
+ return f_string_dynamic_terminate_after(destination);
+ }
+#endif // _di_controller_string_dynamic_append_terminated_
+
+#ifndef _di_controller_string_dynamic_partial_append_terminated_
+ f_status_t controller_dynamic_partial_append_terminated(const f_string_static_t source, const f_string_range_t range, f_string_dynamic_t *destination) {
+
+ f_status_t status = f_string_dynamic_partial_append(source, range, destination);
+ if (F_status_is_error(status)) return status;
+
+ return f_string_dynamic_terminate_after(destination);
+ }
+#endif // _di_controller_string_dynamic_partial_append_terminated_
+
+#ifndef _di_controller_file_load_
+ f_status_t controller_file_load(const controller_global_t global, const bool required, const f_string_t path_prefix, const f_string_static_t path_name, const f_string_t path_suffix, const f_array_length_t path_prefix_length, const f_array_length_t path_suffix_length, controller_cache_t * const cache) {
+
+ f_status_t status = F_none;
+ f_file_t file = f_file_t_initialize;
+
+ cache->action.name_file.used = 0;
+ cache->buffer_file.used = 0;
+
+ macro_f_time_spec_t_clear(cache->timestamp);
+
+ status = f_string_append(path_prefix, path_prefix_length, &cache->action.name_file);
+
+ if (F_status_is_error_not(status)) {
+ status = f_string_append(f_path_separator_s, F_path_separator_s_length, &cache->action.name_file);
+ }
+
+ if (F_status_is_error_not(status)) {
+ status = f_string_append(path_name.string, path_name.used, &cache->action.name_file);
+ }
+
+ if (F_status_is_error_not(status)) {
+ status = f_string_append(F_path_extension_separator_s, F_path_extension_separator_s_length, &cache->action.name_file);
+ }
+
+ if (F_status_is_error_not(status)) {
+ status = f_string_append(path_suffix, path_suffix_length, &cache->action.name_file);
+ }
+
+ if (F_status_is_error(status)) {
+ if (global.main->error.verbosity != f_console_verbosity_quiet_e) {
+ controller_print_error(global.thread, global.main->error, F_status_set_fine(status), "f_string_append", F_true);
+ }
+
+ return status;
+ }
+
+ status = f_string_dynamic_terminate_after(&cache->action.name_file);
+
+ if (F_status_is_error(status)) {
+ if (global.main->error.verbosity != f_console_verbosity_quiet_e) {
+ controller_print_error(global.thread, global.main->error, F_status_set_fine(status), "f_string_dynamic_terminate_after", F_true);
+ }
+
+ return status;
+ }
+
+ const f_array_length_t path_length = global.setting->path_setting.used ? global.setting->path_setting.used + F_path_separator_s_length + cache->action.name_file.used : cache->action.name_file.used;
+ char path[path_length + 1];
+
+ if (global.setting->path_setting.used) {
+ memcpy(path, global.setting->path_setting.string, global.setting->path_setting.used);
+ memcpy(path + global.setting->path_setting.used + F_path_separator_s_length, cache->action.name_file.string, cache->action.name_file.used);
+
+ path[global.setting->path_setting.used] = f_path_separator_s[0];
+ }
+
+ path[path_length] = 0;
+
+ status = f_file_stream_open(path, 0, &file);
+
+ if (F_status_is_error(status)) {
+ if (!required && F_status_set_fine(status) == F_file_found_not) {
+ f_file_stream_close(F_true, &file);
+
+ return F_file_found_not;
+ }
+
+ if (global.main->error.verbosity != f_console_verbosity_quiet_e) {
+ controller_print_error_file(global.thread, global.main->error, F_status_set_fine(status), "f_file_stream_open", F_true, path, "open", fll_error_file_type_file_e);
+ }
+ }
+ else {
+ status = f_file_stream_read(file, &cache->buffer_file);
+
+ if (F_status_is_error(status)) {
+ if (global.main->error.verbosity != f_console_verbosity_quiet_e) {
+ controller_print_error_file(global.thread, global.main->error, F_status_set_fine(status), "f_file_stream_read", F_true, path, "read", fll_error_file_type_file_e);
+ }
+ }
+ }
+
+ f_file_stream_close(F_true, &file);
+
+ if (F_status_is_error_not(status)) {
+ struct stat stat_file;
+
+ status = f_file_stat(path, F_true, &stat_file);
+
+ if (F_status_is_error(status)) {
+ if (global.main->error.verbosity != f_console_verbosity_quiet_e) {
+ controller_print_error_file(global.thread, global.main->error, F_status_set_fine(status), "f_file_stat", F_true, path, "stat", fll_error_file_type_file_e);
+ }
+ }
+ else {
+ cache->timestamp.seconds = stat_file.st_ctim.tv_sec;
+ cache->timestamp.nanoseconds = stat_file.st_ctim.tv_nsec;
+ }
+ }
+
+ if (F_status_is_error(status)) return status;
+
+ return F_none;
+ }
+#endif // _di_controller_file_load_
+
+#ifndef _di_controller_file_pid_create_
+ f_status_t controller_file_pid_create(const pid_t pid, const f_string_static_t path) {
+
+ f_status_t status = F_none;
+
+ // the file exists, do not attempt to overwrite.
+ if (f_file_exists(path.string) == F_true) {
+ return F_status_set_error(F_file_found);
+ }
+
+ {
+ f_string_dynamic_t path_directory = f_string_dynamic_t_initialize;
+
+ status = f_file_name_directory(path.string, path.used, &path_directory);
+
+ if (F_status_is_error_not(status)) {
+ status = f_directory_exists(path_directory.string);
+ }
+
+ macro_f_string_dynamic_t_delete_simple(path_directory)
+
+ if (F_status_is_error(status)) return status;
+
+ // the directory does not exist so do not bother attempting to create a pid file.
+ if (status == F_false) {
+ return F_status_set_error(F_directory_not);
+ }
+ }
+
+ f_file_t file = f_file_t_initialize;
+
+ file.flag = F_file_flag_write_only_d;
+
+ status = f_file_stream_open(path.string, f_file_open_mode_truncate_s, &file);
+ if (F_status_is_error(status)) return status;
+
+ fll_print_format("%i%c", file.stream, pid, f_string_eol_s[0]);
+
+ f_file_stream_close(F_true, &file);
+
+ if (F_status_is_error(status)) return status;
+
+ return F_none;
+ }
+#endif // _di_controller_file_pid_create_
+
+#ifndef _di_controller_file_pid_delete_
+ f_status_t controller_file_pid_delete(const pid_t pid, const f_string_static_t path) {
+
+ // only delete if the file exists and there is no error while checking.
+ if (f_file_exists(path.string) != F_true) {
+ return F_none;
+ }
+
+ f_status_t status = F_none;
+ f_file_t pid_file = f_file_t_initialize;
+
+ status = f_file_stream_open(path.string, f_file_open_mode_read_s, &pid_file);
+ if (F_status_is_error(status)) return status;
+
+ f_string_dynamic_t pid_buffer = f_string_dynamic_t_initialize;
+
+ status = f_file_stream_read(pid_file, &pid_buffer);
+
+ if (F_status_is_error_not(status)) {
+ status = f_file_stream_close(F_true, &pid_file);
+ }
+
+ if (F_status_is_error_not(status)) {
+ f_number_unsigned_t number = 0;
+ f_string_range_t range = macro_f_string_range_t_initialize(pid_buffer.used);
+
+ for (; range.start < pid_buffer.used; ++range.start) {
+ if (!isspace(pid_buffer.string[range.start])) break;
+ } // for
+
+ for (; range.stop > 0; --range.stop) {
+ if (!isspace(pid_buffer.string[range.stop])) break;
+ } // for
+
+ status = fl_conversion_string_to_decimal_unsigned(pid_buffer.string, range, &number);
+
+ if (F_status_is_error_not(status) && number == pid) {
+ status = f_file_remove(path.string);
+ }
+ else {
+ status = F_status_set_error(F_number_not);
+ }
+ }
+
+ macro_f_string_dynamic_t_delete_simple(pid_buffer);
+
+ return status;
+ }
+#endif // _di_controller_file_pid_delete_
+
+#ifndef _di_controller_file_pid_read_
+ f_status_t controller_file_pid_read(const f_string_static_t path, pid_t * const pid) {
+
+ *pid = 0;
+
+ f_status_t status = f_file_exists(path.string);
+ if (F_status_is_error(status)) return status;
+
+ if (status != F_true) {
+ return F_data_not;
+ }
+
+ f_file_t pid_file = f_file_t_initialize;
+
+ status = f_file_stream_open(path.string, f_file_open_mode_read_s, &pid_file);
+ if (F_status_is_error(status)) return status;
+
+ f_string_dynamic_t pid_buffer = f_string_dynamic_t_initialize;
+
+ status = f_file_stream_read(pid_file, &pid_buffer);
+
+ if (F_status_is_error_not(status)) {
+ status = f_file_stream_close(F_true, &pid_file);
+ }
+
+ if (F_status_is_error_not(status)) {
+ f_number_unsigned_t number = 0;
+ f_string_range_t range = macro_f_string_range_t_initialize(pid_buffer.used);
+
+ for (; range.start < pid_buffer.used; ++range.start) {
+ if (!isspace(pid_buffer.string[range.start])) break;
+ } // for
+
+ for (; range.stop > 0; --range.stop) {
+ if (!isspace(pid_buffer.string[range.stop])) break;
+ } // for
+
+ status = fl_conversion_string_to_decimal_unsigned(pid_buffer.string, range, &number);
+
+ if (F_status_is_error_not(status)) {
+ *pid = (pid_t) number;
+ }
+ }
+
+ macro_f_string_dynamic_t_delete_simple(pid_buffer);
+
+ return status;
+ }
+#endif // _di_controller_file_pid_read_
+
+#ifndef _di_controller_get_id_user_
+ f_status_t controller_get_id_user(const f_string_static_t buffer, const f_string_range_t range, controller_cache_t * const cache, uid_t * const id) {
+
+ f_number_unsigned_t number = 0;
+
+ f_status_t status = fl_conversion_string_to_number_unsigned(buffer.string, range, &number);
+
+ if (F_status_is_error(status)) {
+ if (F_status_set_fine(status) == F_number) {
+ cache->action.generic.used = 0;
+
+ status = controller_dynamic_rip_nulless_terminated(buffer, range, &cache->action.generic);
+ if (F_status_is_error(status)) return status;
+
+ status = f_account_id_user_by_name(cache->action.generic.string, id);
+ if (F_status_is_error(status)) return status;
+
+ if (status == F_exist_not) {
+ return F_status_set_error(F_exist_not);
+ }
+
+ return F_none;
+ }
+
+ return status;
+ }
+ else if (number > F_type_size_32_unsigned_d) {
+ return F_status_set_error(F_number_too_large);
+ }
+
+ return status;
+ }
+#endif // _di_controller_get_id_user_
+
+#ifndef _di_controller_get_id_group_
+ f_status_t controller_get_id_group(const f_string_static_t buffer, const f_string_range_t range, controller_cache_t * const cache, gid_t * const id) {
+
+ f_number_unsigned_t number = 0;
+
+ f_status_t status = fl_conversion_string_to_number_unsigned(buffer.string, range, &number);
+
+ if (F_status_is_error(status)) {
+ if (F_status_set_fine(status) == F_number) {
+ cache->action.generic.used = 0;
+
+ status = controller_dynamic_rip_nulless_terminated(buffer, range, &cache->action.generic);
+ if (F_status_is_error(status)) return status;
+
+ status = f_account_id_group_by_name(cache->action.generic.string, id);
+ if (F_status_is_error(status)) return status;
+
+ if (status == F_exist_not) {
+ return F_status_set_error(F_exist_not);
+ }
+
+ return F_none;
+ }
+
+ return status;
+ }
+ else if (number > F_type_size_32_unsigned_d) {
+ return F_status_set_error(F_number_too_large);
+ }
+
+ return status;
+ }
+#endif // _di_controller_get_id_group_
+
+#ifndef _di_controller_perform_ready_
+ f_status_t controller_perform_ready(const controller_global_t *global, controller_cache_t * const cache, const bool is_entry) {
+
+ if (!is_entry) {
+ return F_none;
+ }
+
+ f_status_t status = F_none;
+
+ if (global->setting->entry.pid != controller_entry_pid_disable_e && !global->setting->path_pid.used) {
+ if (global->main->parameters[controller_parameter_validate_e].result == f_console_result_additional_e) {
+ status = controller_file_pid_create(global->main->pid, global->setting->path_pid);
+ }
+
+ // Report pid file error but because this could be an "init" program, consider the pid file as optional and continue on.
+ if (F_status_is_error(status)) {
+
+ // Always return immediately on memory errors.
+ if (F_status_set_fine(status) == F_memory_not) {
+ if (global->main->error.verbosity != f_console_verbosity_quiet_e) {
+ controller_lock_print(global->main->error.to, global->thread);
+
+ controller_print_error_file(0, global->main->error, F_status_set_fine(status), "controller_file_pid_create", F_true, global->setting->path_pid.string, "create", fll_error_file_type_file_e);
+
+ flockfile(global->main->error.to.stream);
+
+ controller_entry_print_error_cache(is_entry, global->main->error, cache->action);
+
+ controller_unlock_print_flush(global->main->error.to, global->thread);
+ }
+
+ return status;
+ }
+
+ if (global->main->warning.verbosity == f_console_verbosity_debug_e) {
+ controller_lock_print(global->main->warning.to, global->thread);
+
+ if (F_status_set_fine(status) == F_read_only) {
+ fl_print_format("%c%[%SThe pid file '%]", global->main->warning.to.stream, f_string_eol_s[0], global->main->warning.context, global->main->warning.prefix, global->main->warning.context);
+ fl_print_format("%[%Q%]", global->main->warning.to.stream, global->main->warning.notable, global->setting->path_pid, global->main->warning.notable);
+ fl_print_format("%[' could not be written because the destination is read only.%]%c", global->main->warning.to.stream, global->main->warning.context, global->main->warning.context, f_string_eol_s[0]);
+ }
+ else {
+ controller_print_error_file(0, global->main->warning, F_status_set_fine(status), "controller_file_pid_create", F_true, global->setting->path_pid.string, "create", fll_error_file_type_file_e);
+ }
+
+ controller_entry_print_error_cache(is_entry, global->main->warning, cache->action);
+
+ controller_unlock_print_flush(global->main->warning.to, global->thread);
+ }
+
+ status = F_none;
+ }
+ else {
+ global->setting->pid_created = F_true;
+
+ if (global->main->output.verbosity == f_console_verbosity_debug_e) {
+ controller_lock_print(global->main->output.to, global->thread);
+
+ fl_print_format("%cPID file '", global->main->output.to.stream, f_string_eol_s[0]);
+ fl_print_format("%[%Q%]", global->main->output.to.stream, global->main->context.set.notable, global->setting->path_pid, global->main->context.set.notable);
+
+ if (global->main->parameters[controller_parameter_validate_e].result == f_console_result_none_e) {
+ fl_print_format("' created.%c", global->main->output.to.stream, f_string_eol_s[0]);
+ }
+ else {
+ fl_print_format("'.%c", global->main->output.to.stream, f_string_eol_s[0]);
+ }
+
+ controller_unlock_print_flush(global->main->output.to, global->thread);
+ }
+ }
+ }
+
+ if (global->setting->path_control.used) {
+ if (global->setting->control_readonly) {
+ if (f_file_exists(global->setting->path_control.string) != F_true) {
+ if (global->main->output.verbosity == f_console_verbosity_debug_e) {
+ controller_lock_print(global->main->output.to, global->thread);
+
+ fl_print_format("%c%[%SControl socket '%]", global->main->warning.to.stream, f_string_eol_s[0], global->main->warning.context, global->main->warning.prefix, global->main->warning.context);
+ fl_print_format("%[%Q%]", global->main->output.to.stream, global->main->context.set.notable, global->setting->path_control, global->main->context.set.notable);
+ fl_print_format("' .%c", global->main->output.to.stream, f_string_eol_s[0]);
+ fl_print_format("%[' cannot be found while read only mode is enabled and so the Control socket is unavailable.%]%c", global->main->output.to.stream, global->main->warning.context, global->main->warning.context, f_string_eol_s[0]);
+
+ controller_unlock_print_flush(global->main->output.to, global->thread);
+ }
+
+ return status;
+ }
+ }
+ else {
+ status = f_socket_create(&global->setting->control_socket);
+
+ if (F_status_is_error(status)) {
+ if (F_status_set_fine(status) == F_memory_not) {
+ controller_print_error(global->thread, global->main->error, F_status_set_fine(status), "f_socket_create", F_true);
+
+ return status;
+ }
+
+ if (global->main->output.verbosity == f_console_verbosity_debug_e) {
+ controller_lock_print(global->main->output.to, global->thread);
+
+ fl_print_format("%c%[%SControl socket '%]", global->main->warning.to.stream, f_string_eol_s[0], global->main->warning.context, global->main->warning.prefix, global->main->warning.context);
+ fl_print_format("%[%Q%]", global->main->output.to.stream, global->main->context.set.notable, global->setting->path_control, global->main->context.set.notable);
+ fl_print_format("%[' could not be created, code %]", global->main->output.to.stream, global->main->warning.context, global->main->warning.context);
+ fl_print_format("%[%ui%]", global->main->output.to.stream, global->main->context.set.notable, F_status_set_fine(status), global->main->context.set.notable);
+ fl_print_format("%[.%]%c", global->main->output.to.stream, global->main->warning.context, global->main->warning.context, f_string_eol_s[0]);
+
+ controller_unlock_print_flush(global->main->output.to, global->thread);
+ }
+ }
+ else {
+ status = f_file_remove(global->setting->path_control.string);
+
+ if (F_status_set_fine(status) == F_memory_not) {
+ controller_print_error(global->thread, global->main->error, F_status_set_fine(status), "f_file_remove", F_true);
+
+ return status;
+ }
+
+ global->setting->control_socket.name = global->setting->path_control.string;
+
+ status = f_socket_bind_file(global->setting->control_socket);
+
+ if (F_status_is_error(status)) {
+ f_socket_disconnect(&global->setting->control_socket, f_socket_close_fast_e);
+
+ if (F_status_set_fine(status) == F_memory_not) {
+ controller_print_error(global->thread, global->main->error, F_status_set_fine(status), "f_socket_bind_file", F_true);
+
+ return status;
+ }
+
+ if (global->main->output.verbosity == f_console_verbosity_debug_e) {
+ controller_lock_print(global->main->output.to, global->thread);
+
+ fl_print_format("%c%[%SControl socket '%]", global->main->warning.to.stream, f_string_eol_s[0], global->main->warning.context, global->main->warning.prefix, global->main->warning.context);
+ fl_print_format("%[%Q%]", global->main->output.to.stream, global->main->context.set.notable, global->setting->path_control, global->main->context.set.notable);
+ fl_print_format("%[' could not be bound, code %]", global->main->output.to.stream, global->main->warning.context, global->main->warning.context);
+ fl_print_format("%[%ui%]", global->main->output.to.stream, global->main->context.set.notable, F_status_set_fine(status), global->main->context.set.notable);
+ fl_print_format("%[.%]%c", global->main->output.to.stream, global->main->warning.context, global->main->warning.context, f_string_eol_s[0]);
+
+ controller_unlock_print_flush(global->main->output.to, global->thread);
+ }
+ }
+ else {
+ status = f_file_role_change(global->setting->path_control.string, global->setting->control_user, global->setting->control_group, F_true);
+
+ if (F_status_is_error(status)) {
+ f_socket_disconnect(&global->setting->control_socket, f_socket_close_fast_e);
+
+ if (F_status_set_fine(status) == F_memory_not) {
+ controller_print_error(global->thread, global->main->error, F_status_set_fine(status), "f_file_role_change", F_true);
+
+ return status;
+ }
+
+ if (global->main->output.verbosity == f_console_verbosity_debug_e) {
+ controller_lock_print(global->main->output.to, global->thread);
+
+ fl_print_format("%c%[%SControl socket '%]", global->main->warning.to.stream, f_string_eol_s[0], global->main->warning.context, global->main->warning.prefix, global->main->warning.context);
+ fl_print_format("%[%Q%]", global->main->output.to.stream, global->main->context.set.notable, global->setting->path_control, global->main->context.set.notable);
+ fl_print_format("%[' failed to set file roles, code %]", global->main->output.to.stream, global->main->warning.context, global->main->warning.context);
+ fl_print_format("%[%ui%]", global->main->output.to.stream, global->main->context.set.notable, F_status_set_fine(status), global->main->context.set.notable);
+ fl_print_format("%[.%]%c", global->main->output.to.stream, global->main->warning.context, global->main->warning.context, f_string_eol_s[0]);
+
+ controller_unlock_print_flush(global->main->output.to, global->thread);
+ }
+ }
+ else {
+ status = f_file_mode_set(global->setting->path_control.string, global->setting->control_mode);
+
+ if (F_status_is_error(status)) {
+ f_socket_disconnect(&global->setting->control_socket, f_socket_close_fast_e);
+
+ if (F_status_set_fine(status) == F_memory_not) {
+ controller_print_error(global->thread, global->main->error, F_status_set_fine(status), "f_file_role_change", F_true);
+
+ return status;
+ }
+
+ if (global->main->output.verbosity == f_console_verbosity_debug_e) {
+ controller_lock_print(global->main->output.to, global->thread);
+
+ fl_print_format("%c%[%SControl socket '%]", global->main->warning.to.stream, f_string_eol_s[0], global->main->warning.context, global->main->warning.prefix, global->main->warning.context);
+ fl_print_format("%[%Q%]", global->main->output.to.stream, global->main->context.set.notable, global->setting->path_control, global->main->context.set.notable);
+ fl_print_format("%[' failed to set file mode, code %]", global->main->output.to.stream, global->main->warning.context, global->main->warning.context);
+ fl_print_format("%[%ui%]", global->main->output.to.stream, global->main->context.set.notable, F_status_set_fine(status), global->main->context.set.notable);
+ fl_print_format("%[.%]%c", global->main->output.to.stream, global->main->warning.context, global->main->warning.context, f_string_eol_s[0]);
+
+ controller_unlock_print_flush(global->main->output.to, global->thread);
+ }
+ }
+ else {
+ if (global->main->output.verbosity == f_console_verbosity_debug_e) {
+ controller_lock_print(global->main->output.to, global->thread);
+
+ fl_print_format("%cControl socket '", global->main->output.to.stream, f_string_eol_s[0]);
+ fl_print_format("%[%Q%]", global->main->output.to.stream, global->main->context.set.notable, global->setting->path_control, global->main->context.set.notable);
+
+ if (global->main->parameters[controller_parameter_validate_e].result == f_console_result_none_e) {
+ fl_print_format("' created.%c", global->main->output.to.stream, f_string_eol_s[0]);
+ }
+ else {
+ fl_print_format("'.%c", global->main->output.to.stream, f_string_eol_s[0]);
+ }
+
+ controller_unlock_print_flush(global->main->output.to, global->thread);
+ }
+
+ status = f_thread_create(0, &global->thread->id_listen, &controller_thread_control_listen, (void *) global);
+
+ if (status == F_child) {
+ return status;
+ }
+
+ if (F_status_is_error_not(status)) {
+ status = f_thread_create(0, &global->thread->id_control, &controller_thread_control, (void *) global);
+
+ if (status == F_child) {
+ return status;
+ }
+ }
+
+ if (F_status_is_error(status)) {
+ if (global->thread->id_listen) {
+ f_thread_cancel(global->thread->id_listen);
+ f_thread_join(global->thread->id_listen, 0);
+
+ global->thread->id_listen = 0;
+ }
+
+ global->thread->id_control = 0;
+
+ if (global->main->error.verbosity != f_console_verbosity_quiet_e) {
+ controller_print_error(global->thread, global->main->error, F_status_set_fine(status), "f_thread_create", F_true);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Don't fail if unable to create socket file.
+ status = F_none;
+ }
+ }
+
+ return status;
+ }
+#endif // _di_controller_perform_ready_
+
+#ifndef _di_controller_status_simplify_error_
+ f_status_t controller_status_simplify_error(const f_status_t status) {
+
+ if (status == F_memory_not) {
+ return F_status_set_error(F_memory);
+ }
+
+ if (status == F_file_open_max || status == F_space_not || status == F_busy) {
+ return F_status_set_error(F_resource);
+ }
+
+ if (status == F_access_denied || status == F_filesystem_quota_block || status == F_prohibited || status == F_input_output) {
+ return F_status_set_error(F_access);
+ }
+
+ if (status == F_complete_not_utf || status == F_complete_not_utf_block || status == F_complete_not_utf_eof || status == F_complete_not_utf_eol || status == F_complete_not_utf_eos || status == F_complete_not_utf_stop) {
+ return F_status_set_error(F_encoding);
+ }
+
+ if (status == F_number || status == F_number_negative || status == F_number_positive || status == F_number_overflow) {
+ return F_status_set_error(F_number);
+ }
+
+ if (status == F_parameter || status == F_found_not || status == F_interrupt || status == F_supported_not || status == F_critical) {
+ return F_status_set_error(status);
+ }
+
+ if (status == F_valid_not) {
+ return F_status_set_error(F_valid_not);
+ }
+
+ return F_status_set_error(F_failure);
+ }
+#endif // _di_controller_status_simplify_error_
+
+#ifndef _di_controller_time_
+ void controller_time(const time_t seconds, const long nanoseconds, struct timespec *time) {
+
+ struct timeval now;
+
+ gettimeofday(&now, 0);
+
+ time->tv_sec = now.tv_sec + seconds;
+ time->tv_nsec = (now.tv_usec * 1000) + nanoseconds;
+
+ // If tv_nsec is 1 second or greater, then increment seconds.
+ if (time->tv_nsec >= 1000000000) {
+ ++(time->tv_sec);
+
+ time->tv_nsec -= 1000000000;
+ }
+ }
+#endif // _di_controller_time_
+
+#ifndef _di_controller_time_milliseconds_
+ struct timespec controller_time_milliseconds(const f_number_unsigned_t milliseconds) {
+
+ struct timespec time;
+ time.tv_sec = milliseconds > 1000 ? milliseconds / 1000 : 0;
+ time.tv_nsec = (time.tv_sec ? milliseconds - time.tv_sec : milliseconds) * 1000;
+
+ return time;
+ }
+#endif // _di_controller_time_milliseconds_
+
+#ifndef _di_controller_time_seconds_
+ struct timespec controller_time_seconds(const f_number_unsigned_t seconds) {
+
+ struct timespec time;
+ time.tv_sec = seconds;
+ time.tv_nsec = 0;
+
+ return time;
+ }
+#endif // _di_controller_time_seconds_
+
+#ifndef _di_controller_time_sleep_nanoseconds_
+ int controller_time_sleep_nanoseconds(controller_main_t * const main, controller_setting_t * const setting, struct timespec time) {
+
+ // When sleep is a second or more, instead wait for terminating signals if interruptible.
+ if (setting->interruptible && time.tv_sec) {
+ siginfo_t information;
+ f_signal_t signal = f_signal_t_initialize;
+
+ memset(&information, 0, sizeof(siginfo_t));
+
+ f_signal_set_empty(&signal.set);
+ f_signal_set_add(F_signal_abort, &signal.set);
+ f_signal_set_add(F_signal_interrupt, &signal.set);
+ f_signal_set_add(F_signal_quit, &signal.set);
+ f_signal_set_add(F_signal_termination, &signal.set);
+
+ if (F_status_is_error(f_signal_wait_until(&signal.set, &time, &information))) {
+ return -1;
+ }
+
+ return 0;
+ }
+
+ return nanosleep(&time, 0);
+ }
+#endif // _di_controller_time_sleep_nanoseconds_
+
+#ifndef _di_controller_validate_define_name_
+ f_status_t controller_validate_environment_name(const f_string_static_t name) {
+
+ if (!name.used) return F_none;
+
+ f_status_t status = F_none;
+
+ if (name.string[0] != '_') {
+ status = f_utf_is_alpha(name.string, name.used);
+
+ if (F_status_is_error(status)) return status;
+ if (status == F_false) return F_false;
+ }
+
+ for (f_array_length_t i = macro_f_utf_byte_width(name.string[0]); i < name.used; i += macro_f_utf_byte_width(name.string[i])) {
+
+ if (name.string[i] == '_') continue;
+
+ status = f_utf_is_alpha_digit(name.string, name.used);
+
+ if (F_status_is_error(status)) return status;
+ if (status == F_false) return F_false;
+ } // for
+
+ return F_true;
+ }
+#endif // _di_controller_validate_define_name_
+
+#ifndef _di_controller_validate_has_graph_
+ f_status_t controller_validate_has_graph(const f_string_static_t name) {
+
+ if (!name.used) return F_none;
+
+ f_status_t status = F_none;
+
+ for (f_array_length_t i = 0; i < name.used; i += macro_f_utf_byte_width(name.string[i])) {
+
+ status = f_utf_is_graph(name.string, name.used);
+
+ if (F_status_is_error(status)) return status;
+ if (status == F_true) return F_true;
+ } // for
+
+ return F_false;
+ }
+#endif // _di_controller_validate_has_graph_
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
--- /dev/null
+/**
+ * FLL - Level 3
+ *
+ * Project: Controller
+ * API Version: 0.5
+ * Licenses: lgpl-2.1-or-later
+ */
+#ifndef _PRIVATE_controller_h
+#define _PRIVATE_controller_h
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Given a string whose range represents a number, seek past the first positive or negative sign.
+ *
+ * This will stop at the first non-NULL, non-'+' and non-'-' characters.
+ *
+ * Only the first '+' or '-' are processed.
+ *
+ * @param buffer
+ * The string referenced by the range.
+ * @param range
+ * The range within the buffer to process.
+ *
+ * @return
+ * The string range.
+ * The start range will be past the stop range on overflow or on any failure.
+ */
+#ifndef _di_controller_range_after_number_sign_
+ extern f_string_range_t controller_range_after_number_sign(const f_string_static_t buffer, const f_string_range_t range) F_attribute_visibility_internal_d;
+#endif // _di_controller_range_after_number_sign_
+
+/**
+ * Rip a string from the source and then add a NULL after the end of the string.
+ *
+ * @param source
+ * The string to copy from.
+ * @param destination
+ * The string to copy to.
+ *
+ * @return
+ * F_none on success.
+ *
+ * Errors (with error bit) from: f_string_dynamic_terminate_after().
+ * Errors (with error bit) from: fl_string_dynamic_rip_nulless().
+ *
+ * @see f_string_dynamic_terminate_after()
+ * @see fl_string_dynamic_rip_nulless()
+ */
+#ifndef _di_controller_string_dynamic_rip_nulless_terminated_
+ extern f_status_t controller_dynamic_rip_nulless_terminated(const f_string_static_t source, const f_string_range_t range, f_string_dynamic_t *destination) F_attribute_visibility_internal_d;
+#endif // _di_controller_string_dynamic_rip_nulless_terminated_
+
+/**
+ * Append a string and then add a NULL after the end of the string.
+ *
+ * @param source
+ * The string to copy from.
+ * @param destination
+ * The string to copy to.
+ *
+ * @return
+ * F_none on success.
+ *
+ * Errors (with error bit) from: f_string_dynamic_append_nulless().
+ * Errors (with error bit) from: f_string_dynamic_terminate_after().
+ *
+ * @see f_string_dynamic_append_nulless()
+ * @see f_string_dynamic_terminate_after()
+ */
+#ifndef _di_controller_string_dynamic_append_terminated_
+ extern f_status_t controller_dynamic_append_terminated(const f_string_static_t from, f_string_dynamic_t *destination) F_attribute_visibility_internal_d;
+#endif // _di_controller_string_dynamic_append_terminated_
+
+/**
+ * Append given range from within a string and then add a NULL after the end of the string.
+ *
+ * @param from
+ * The string to copy from.
+ * @param range
+ * The range within the from string to copy.
+ * @param destination
+ * The string to copy to.
+ *
+ * @return
+ * F_none on success.
+ *
+ * Errors (with error bit) from: f_string_dynamic_append().
+ * Errors (with error bit) from: f_string_dynamic_terminate_after().
+ *
+ * @see f_string_dynamic_append()
+ * @see f_string_dynamic_terminate_after()
+ */
+#ifndef _di_controller_string_dynamic_partial_append_terminated_
+ extern f_status_t controller_dynamic_partial_append_terminated(const f_string_static_t from, const f_string_range_t range, f_string_dynamic_t *destination) F_attribute_visibility_internal_d;
+#endif // _di_controller_string_dynamic_partial_append_terminated_
+
+/**
+ * Load a file from the controller settings directory.
+ *
+ * @param global
+ * The global data.
+ * @param required
+ * If TRUE, the file is required to exist and will throw an error if not found.
+ * If FALSE, the file is not required to exist and will return without error if not found.
+ * @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 cache
+ * The following within the cache is updated:
+ * - name_file: The partial path of the file is inserted.
+ * - buffer_file: The contents of the file is inserted.
+ * - timestamp: This is updated to reflect the last changed timestamp.
+ *
+ * @return
+ * F_none on success.
+ * F_file_found_not if required is FALSE and the file is not found.
+ *
+ * Errors (with error bit) from: f_file_stat().
+ * Errors (with error bit) from: f_file_stream_open().
+ * Errors (with error bit) from: f_file_stream_read().
+ * Errors (with error bit) from: f_string_append().
+ * Errors (with error bit) from: f_string_dynamic_terminate_after().
+ *
+ * @see f_file_stat()
+ * @see f_file_stream_open()
+ * @see f_file_stream_read()
+ * @see f_string_append()
+ * @see f_string_dynamic_terminate_after()
+ */
+#ifndef _di_controller_file_load_
+ extern f_status_t controller_file_load(const controller_global_t global, const bool required, const f_string_t path_prefix, const f_string_static_t path_name, const f_string_t path_suffix, const f_array_length_t path_prefix_length, const f_array_length_t path_suffix_length, controller_cache_t * const cache) F_attribute_visibility_internal_d;
+#endif // _di_controller_file_load_
+
+/**
+ * Create the pid file, if possible.
+ *
+ * @param pid
+ * The PID (process id).
+ * @param path
+ * The file path to the pid file to create.
+ *
+ * @return
+ * F_none on success.
+ * F_access_denied if pid file is not created due to access denied errors.
+ * F_directory_not if pid file is not created due to a parent directory is unavailable or invalid.
+ *
+ * Errors (with error bit) from: f_directory_exists().
+ * Errors (with error bit) from: f_file_name_directory().
+ * Errors (with error bit) from: f_file_stream_open().
+ *
+ * @see f_directory_exists()
+ * @see f_file_name_directory()
+ * @see f_file_stream_open()
+ */
+#ifndef _di_controller_file_pid_create_
+ f_status_t controller_file_pid_create(const pid_t pid, const f_string_static_t path) F_attribute_visibility_internal_d;
+#endif // _di_controller_file_pid_create_
+
+/**
+ * Delete the pid file, if exists and is valid.
+ *
+ * This is meant to be called on exit and avoids checking status codes, returning void.
+ *
+ * @param pid
+ * The PID (process id).
+ * @param path
+ * The file path to the pid file to create.
+ *
+ * @return
+ * F_none on success.
+ *
+ * F_number_not (with error bit) if the number from the pid file doesn't match the expected pid.
+ *
+ * Errors (with error bit) from: f_file_stream_close().
+ * Errors (with error bit) from: f_file_stream_open().
+ * Errors (with error bit) from: f_file_stream_read().
+ * Errors (with error bit) from: fl_conversion_string_to_decimal_unsigned()
+ */
+#ifndef _di_controller_file_pid_delete_
+ f_status_t controller_file_pid_delete(const pid_t pid, const f_string_static_t path) F_attribute_visibility_internal_d;
+#endif // _di_controller_file_pid_delete_
+
+/**
+ * Read the PID from a PID file.
+ *
+ * @param path
+ * The file path to the pid file to create.
+ * @param pid
+ * The PID to be read.
+ *
+ * @return
+ * F_none on success.
+ *
+ * Errors (with error bit) from: f_file_stream_close().
+ * Errors (with error bit) from: f_file_stream_open().
+ * Errors (with error bit) from: f_file_stream_read().
+ * Errors (with error bit) from: fl_conversion_string_to_decimal_unsigned()
+ */
+#ifndef _di_controller_file_pid_read_
+ f_status_t controller_file_pid_read(const f_string_static_t path, pid_t * const pid) F_attribute_visibility_internal_d;
+#endif // _di_controller_file_pid_read_
+
+/**
+ * Convert the string from a string representation of an ID or a user name into the numeric representation of that ID or user name.
+ *
+ * @param buffer
+ * A string containing user name or ID.
+ * @param range
+ * The range within the buffer specifically containing the name or ID.
+ * @param cache
+ * The cache.
+ * @param id
+ * The determined user ID.
+ *
+ * @return
+ * F_none on success.
+ * F_exist_not (with error bit) if failed to match the name to an ID.
+ * F_number_too_large (with error bit) if the given ID is too large.
+ *
+ * Errors (with error bit) from: f_account_id_user_by_name().
+ * Errors (with error bit) from: fl_conversion_string_to_number_unsigned().
+ *
+ * @see f_account_id_user_by_name()
+ * @see fl_conversion_string_to_number_unsigned()
+ */
+#ifndef _di_controller_get_id_user_
+ f_status_t controller_get_id_user(const f_string_static_t buffer, const f_string_range_t range, controller_cache_t * const cache, uid_t * const id) F_attribute_visibility_internal_d;
+#endif // _di_controller_get_id_user_
+
+/**
+ * Convert the string from a string representation of an ID or a group name into the numeric representation of that ID or group name.
+ *
+ * @param buffer
+ * A string containing group name or ID.
+ * @param range
+ * The range within the buffer specifically containing the name or ID.
+ * @param cache
+ * The cache.
+ * @param id
+ * The determined group ID.
+ *
+ * @return
+ * F_none on success.
+ * F_exist_not (with error bit) if failed to match the name to an ID.
+ * F_number_too_large (with error bit) if the given ID is too large.
+ *
+ * Errors (with error bit) from: f_account_id_group_by_name().
+ * Errors (with error bit) from: fl_conversion_string_to_number_unsigned().
+ *
+ * @see f_account_id_group_by_name()
+ * @see fl_conversion_string_to_number_unsigned()
+ */
+#ifndef _di_controller_get_id_group_
+ f_status_t controller_get_id_group(const f_string_static_t buffer, const f_string_range_t range, controller_cache_t * const cache, gid_t * const id) F_attribute_visibility_internal_d;
+#endif // _di_controller_get_id_group_
+
+/**
+ * Perform all activities requiring the state to be "ready".
+ *
+ * This prints messages on errors.
+ *
+ * This does not do any locking or unlocking for the setting data, be sure to lock appropriately before and after calling this.
+ *
+ * @param global
+ * The global data.
+ * @param cache
+ * The cache.
+ * @param is_entry
+ * If TRUE, then this operate as an entry.
+ * If FALSE, then this operate as an exit.
+ *
+ * @return
+ * F_none on success.
+ *
+ * Errors from controller_file_pid_create() are not returned, unless it is a memory error.
+ *
+ * @see controller_file_pid_create()
+ */
+#ifndef _di_controller_perform_ready_
+ extern f_status_t controller_perform_ready(const controller_global_t *global, controller_cache_t * const cache, const bool is_entry) F_attribute_visibility_internal_d;
+#endif // _di_controller_perform_ready_
+
+/**
+ * Given a wide range of status codes (that are errors), simplify them down to a small subset.
+ *
+ * @param status
+ * The status code (without the error bit set) to simplify.
+ *
+ * @return
+ * A subset of status codes with error bit.
+ */
+#ifndef _di_controller_status_simplify_error_
+ extern f_status_t controller_status_simplify_error(const f_status_t status) F_attribute_visibility_internal_d;
+#endif // _di_controller_status_simplify_error_
+
+/**
+ * Get the current time, plus the given offset.
+ *
+ * @todo this is basic enough that there needs to be an f_time class with this function f_time_now(), f_time_future(), f_time_past().
+ * "struct timespec" -> f_time_nano_t, "struct timeval" -> f_time_micro_t.
+ *
+ * @param seconds
+ * The seconds to add to current time.
+ * @param nanoseconds
+ * The nanoseconds to add to current time.
+ * @param time
+ * The resulting current time.
+ */
+#ifndef _di_controller_time_
+ void controller_time(const time_t seconds, const long nanoseconds, struct timespec *time) F_attribute_visibility_internal_d;
+#endif // _di_controller_time_
+
+/**
+ * Convert milliseconds to nanoseconds.
+ *
+ * @param milliseconds
+ * The number of milliseconds.
+ *
+ * @return
+ * A time structure suitable for passing to nanosleep() and similar functions.
+ *
+ * @see nanosleep()
+ */
+#ifndef _di_controller_time_milliseconds_
+ extern struct timespec controller_time_milliseconds(const f_number_unsigned_t milliseconds) F_attribute_visibility_internal_d;
+#endif // _di_controller_time_milliseconds_
+
+/**
+ * Convert seconds to nanoseconds.
+ *
+ * @param seconds
+ * The number of seconds.
+ *
+ * @return
+ * A time structure suitable for passing to nanosleep() and similar functions.
+ *
+ * @see nanosleep()
+ */
+#ifndef _di_controller_time_seconds_
+ extern struct timespec controller_time_seconds(const f_number_unsigned_t seconds) F_attribute_visibility_internal_d;
+#endif // _di_controller_time_seconds_
+
+/**
+ * Sleep for a given number of nanoseconds.
+ *
+ * @param main
+ * The main program data.
+ * @param setting
+ * The settings.
+ * @param time
+ * The number of nanoseconds to sleep.
+ *
+ * @return
+ * The result of nanosleep().
+ *
+ * @see nanosleep()
+ */
+#ifndef _di_controller_time_sleep_nanoseconds_
+ extern int controller_time_sleep_nanoseconds(controller_main_t * const main, controller_setting_t * const setting, struct timespec time) F_attribute_visibility_internal_d;
+#endif // _di_controller_time_sleep_nanoseconds_
+
+/**
+ * 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.
+ * Every character after that may be alphanumeric or underscore.
+ * All other characters, including Unicode characters, are invalid.
+ *
+ * @param name
+ * The string to validate.
+ *
+ * @return
+ * F_true on valid.
+ * F_false on invalid.
+ * F_none if there is no string to validate (used = 0).
+ *
+ * Errors (with error bit) from: f_utf_is_alpha().
+ * Errors (with error bit) from: f_utf_is_alpha_digit().
+ *
+ * @see f_utf_is_alpha()
+ * @see f_utf_is_alpha_digit()
+ */
+#ifndef _di_controller_validate_define_name_
+ extern f_status_t controller_validate_environment_name(const f_string_static_t name) F_attribute_visibility_internal_d;
+#endif // _di_controller_validate_define_name_
+
+/**
+ * Validate that the given string has at least one graph character.
+ *
+ * @param name
+ * The string to validate.
+ *
+ * @return
+ * F_true on valid.
+ * F_false on invalid.
+ * F_none if there is no string to validate (used = 0).
+ *
+ * Errors (with error bit) from: f_utf_is_graph().
+ *
+ * @see f_utf_is_graph()
+ */
+#ifndef _di_controller_validate_has_graph_
+ extern f_status_t controller_validate_has_graph(const f_string_static_t name) F_attribute_visibility_internal_d;
+#endif // _di_controller_validate_has_graph_
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // _PRIVATE_controller_h
--- /dev/null
+#include "controller.h"
+#include "../common/private-common.h"
+#include "private-controller_print.h"
+#include "../lock/private-lock_print.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef _di_controller_print_error_
+ void controller_print_error(controller_thread_t * const thread, const fl_print_t print, const f_status_t status, const f_string_t function, const bool fallback) {
+
+ if (print.verbosity == f_console_verbosity_quiet_e) return;
+ if (status == F_interrupt) return;
+
+ // fll_error_print() automatically locks, so manually handle only the mutex locking and flushing rather than calling controller_lock_print().
+ if (thread) {
+ f_thread_mutex_lock(&thread->lock.print);
+ }
+
+ fll_error_print(print, status, function, fallback);
+
+ if (thread) {
+ f_thread_mutex_unlock(&thread->lock.print);
+ }
+ }
+#endif // _di_controller_print_error_
+
+#ifndef _di_controller_print_error_file_
+ void controller_print_error_file(controller_thread_t * const thread, const fl_print_t print, const f_status_t status, const f_string_t function, const bool fallback, const f_string_t name, const f_string_t operation, const uint8_t type) {
+
+ if (print.verbosity == f_console_verbosity_quiet_e) return;
+ if (status == F_interrupt) return;
+
+ // fll_error_print() automatically locks, so manually handle only the mutex locking and flushing rather than calling controller_lock_print().
+ if (thread) {
+ f_thread_mutex_lock(&thread->lock.print);
+ }
+
+ fll_error_file_print(print, status, function, fallback, name, operation, type);
+
+ if (thread) {
+ f_thread_mutex_unlock(&thread->lock.print);
+ }
+ }
+#endif // _di_controller_print_error_file_
+
+#ifndef _di_controller_print_signal_received_
+ void controller_print_signal_received(controller_main_t * const main, const f_status_t signal) {
+
+ if (main->warning.verbosity != f_console_verbosity_verbose_e) return;
+
+ // Must flush and reset color because the interrupt may have interrupted the middle of a print function.
+ fflush(main->warning.to.stream);
+
+ flockfile(main->warning.to.stream);
+
+ fl_print_format("%]%c%c%[Received signal code %]", main->warning.to.stream, main->context.set.reset, f_string_eol_s[0], f_string_eol_s[0], main->context.set.warning, main->context.set.warning);
+ fl_print_format("%[%i%]", main->warning.to.stream, main->context.set.notable, signal, main->context.set.notable);
+ fl_print_format("%[.%]%c", main->warning.to.stream, main->context.set.warning, main->context.set.warning, f_string_eol_s[0]);
+
+ funlockfile(main->warning.to.stream);
+ }
+#endif // _di_controller_print_signal_received_
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
--- /dev/null
+/**
+ * FLL - Level 3
+ *
+ * Project: Controller
+ * API Version: 0.5
+ * Licenses: lgpl-2.1-or-later
+ */
+#ifndef _PRIVATE_controller_print_h
+#define _PRIVATE_controller_print_h
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Print the error, locking the print mutex during the print.
+ *
+ * @param thread
+ * (optional) The thread data.
+ * Set to NULL to disable locking on the thread (this should be done only if the lock is already in place).
+ * @param print
+ * Designates how printing is to be performed.
+ * @param status
+ * The status code to process.
+ * Make sure this has F_status_set_fine() called if the status code has any error or warning bits.
+ * @param function
+ * The name of the function where the error happened.
+ * Set to 0 to disable.
+ * @param fallback
+ * Set to F_true to print the fallback error message for unknown errors.
+ *
+ * @see fll_error_print()
+ */
+#ifndef _di_controller_print_error_
+ extern void controller_print_error(controller_thread_t * const thread, const fl_print_t print, const f_status_t status, const f_string_t function, const bool fallback) F_attribute_visibility_internal_d;
+#endif // _di_controller_print_error_
+
+/**
+ * Print the file error, locking the print mutex during the print.
+ *
+ * @param thread
+ * (optional) The thread data.
+ * Set to NULL to disable locking on the thread (this should be done only if the lock is already in place).
+ * @param print
+ * Designates how printing is to be performed.
+ * @param status
+ * The status code to process.
+ * Make sure this has F_status_set_fine() called if the status code has any error or warning bits.
+ * @param function
+ * The name of the function where the error happened.
+ * Set to 0 to disable.
+ * @param fallback
+ * Set to F_true to print the fallback error message for unknown errors.
+ * @param name
+ * The name of the file or directory.
+ * @param operation
+ * The operation that fails, such as 'create' or 'access'.
+ * @param type
+ * A valid file type code from the fll_error_file_type enum.
+ *
+ * @see fll_error_file_print()
+ */
+#ifndef _di_controller_print_error_file_
+ extern void controller_print_error_file(controller_thread_t * const thread, const fl_print_t print, const f_status_t status, const f_string_t function, const bool fallback, const f_string_t name, const f_string_t operation, const uint8_t type) F_attribute_visibility_internal_d;
+#endif // _di_controller_print_error_file_
+
+/**
+ * Print a message about a process signal being recieved, such as an interrupt signal.
+ *
+ * @param main
+ * The main program data.
+ * @param signal
+ * The signal received.
+ */
+#ifndef _di_controller_print_signal_received_
+ extern void controller_print_signal_received(controller_main_t * const main, const f_status_t signal) F_attribute_visibility_internal_d;
+#endif // _di_controller_print_signal_received_
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // _PRIVATE_controller_print_h
--- /dev/null
+#include "../controller/controller.h"
+#include "../common/private-common.h"
+#include "../controller/private-controller.h"
+#include "../controller/private-controller_print.h"
+#include "../entry/private-entry.h"
+#include "../entry/private-entry_print.h"
+#include "../lock/private-lock.h"
+#include "../lock/private-lock_print.h"
+#include "../rule/private-rule.h"
+#include "../thread/private-thread.h"
+#include "../thread/private-thread_process.h"
+#include "../thread/private-thread_signal.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef _di_controller_entry_action_type_is_rule_
+ f_status_t controller_entry_action_type_is_rule(uint8_t type) {
+
+ switch (type) {
+ case controller_entry_action_type_freeze_e:
+ case controller_entry_action_type_kill_e:
+ case controller_entry_action_type_pause_e:
+ case controller_entry_action_type_reload_e:
+ case controller_entry_action_type_restart_e:
+ case controller_entry_action_type_resume_e:
+ case controller_entry_action_type_start_e:
+ case controller_entry_action_type_stop_e:
+ case controller_entry_action_type_thaw_e:
+ return F_true;
+ }
+
+ return F_false;
+ }
+#endif // _di_controller_entry_action_type_is_rule_
+
+#ifndef _di_controller_entry_action_type_name_
+ f_string_static_t controller_entry_action_type_name(const uint8_t type) {
+
+ f_string_static_t buffer = f_string_static_t_initialize;
+
+ switch (type) {
+ case controller_entry_action_type_consider_e:
+ buffer.string = controller_consider_s;
+ buffer.used = controller_consider_s_length;
+ break;
+
+ case controller_entry_action_type_execute_e:
+ buffer.string = controller_execute_s;
+ buffer.used = controller_execute_s_length;
+ break;
+
+ case controller_entry_action_type_failsafe_e:
+ buffer.string = controller_failsafe_s;
+ buffer.used = controller_failsafe_s_length;
+ break;
+
+ case controller_entry_action_type_freeze_e:
+ buffer.string = controller_freeze_s;
+ buffer.used = controller_freeze_s_length;
+ break;
+
+ case controller_entry_action_type_item_e:
+ buffer.string = controller_item_s;
+ buffer.used = controller_item_s_length;
+ break;
+
+ case controller_entry_action_type_kill_e:
+ buffer.string = controller_kill_s;
+ buffer.used = controller_kill_s_length;
+ break;
+
+ case controller_entry_action_type_pause_e:
+ buffer.string = controller_pause_s;
+ buffer.used = controller_pause_s_length;
+ break;
+
+ case controller_entry_action_type_ready_e:
+ buffer.string = controller_ready_s;
+ buffer.used = controller_ready_s_length;
+ break;
+
+ case controller_entry_action_type_reload_e:
+ buffer.string = controller_reload_s;
+ buffer.used = controller_reload_s_length;
+ break;
+
+ case controller_entry_action_type_restart_e:
+ buffer.string = controller_restart_s;
+ buffer.used = controller_restart_s_length;
+ break;
+
+ case controller_entry_action_type_resume_e:
+ buffer.string = controller_resume_s;
+ buffer.used = controller_resume_s_length;
+ break;
+
+ case controller_entry_action_type_start_e:
+ buffer.string = controller_start_s;
+ buffer.used = controller_start_s_length;
+ break;
+
+ case controller_entry_action_type_stop_e:
+ buffer.string = controller_stop_s;
+ buffer.used = controller_stop_s_length;
+ break;
+
+ case controller_entry_action_type_thaw_e:
+ buffer.string = controller_thaw_s;
+ buffer.used = controller_thaw_s_length;
+ break;
+
+ case controller_entry_action_type_timeout_e:
+ buffer.string = controller_timeout_s;
+ buffer.used = controller_timeout_s_length;
+ break;
+ }
+
+ buffer.size = buffer.used;
+
+ return buffer;
+ }
+#endif // _di_controller_entry_action_type_name_
+
+#ifndef _di_controller_entry_action_type_to_rule_action_type_
+ uint8_t controller_entry_action_type_to_rule_action_type(uint8_t type) {
+
+ switch (type) {
+ case controller_entry_action_type_freeze_e:
+ return controller_rule_action_type_freeze_e;
+
+ case controller_entry_action_type_kill_e:
+ return controller_rule_action_type_kill_e;
+
+ case controller_entry_action_type_pause_e:
+ return controller_rule_action_type_pause_e;
+
+ case controller_entry_action_type_reload_e:
+ return controller_rule_action_type_reload_e;
+
+ case controller_entry_action_type_restart_e:
+ return controller_rule_action_type_restart_e;
+
+ case controller_entry_action_type_resume_e:
+ return controller_rule_action_type_resume_e;
+
+ case controller_entry_action_type_start_e:
+ return controller_rule_action_type_start_e;
+
+ case controller_entry_action_type_stop_e:
+ return controller_rule_action_type_stop_e;
+
+ case controller_entry_action_type_thaw_e:
+ return controller_rule_action_type_thaw_e;
+ }
+
+ return 0;
+ }
+#endif // _di_controller_entry_action_type_to_rule_action_type_
+
+#ifndef _di_controller_entry_actions_read_
+ f_status_t controller_entry_actions_read(const controller_global_t global, const bool is_entry, const f_string_range_t content_range, controller_cache_t * const 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 = cache->object_actions.size;
+
+ while (cache->object_actions.used) {
+
+ cache->object_actions.array[--cache->object_actions.used].start = 1;
+ cache->object_actions.array[cache->object_actions.used].stop = 0;
+ } // while
+
+ cache->content_actions.used = cache->content_actions.size;
+
+ while (cache->content_actions.used) {
+ cache->content_actions.array[--cache->content_actions.used].used = 0;
+ } // while
+
+ {
+ controller_state_interrupt_t custom = macro_controller_state_interrupt_t_initialize(is_entry, global.thread);
+ f_state_t state = macro_f_state_t_initialize(controller_common_allocation_large_d, controller_common_allocation_small_d, 0, &controller_thread_signal_state_fss, 0, (void *) &custom, 0);
+ f_string_range_t range = content_range;
+
+ status = fll_fss_extended_read(cache->buffer_file, state, &range, &cache->object_actions, &cache->content_actions, 0, 0, &cache->delimits, 0);
+ }
+
+ if (F_status_is_error(status)) {
+ controller_entry_print_error(is_entry, global.main->error, cache->action, F_status_set_fine(status), "fll_fss_extended_read", F_true, global.thread);
+
+ return status;
+ }
+
+ status = fl_fss_apply_delimit(cache->delimits, &cache->buffer_file);
+
+ if (F_status_is_error(status)) {
+ controller_entry_print_error(is_entry, global.main->error, cache->action, F_status_set_fine(status), "fl_fss_apply_delimit", F_true, global.thread);
+
+ return status;
+ }
+
+ cache->delimits.used = 0;
+
+ status = controller_entry_actions_increase_by(cache->object_actions.used, actions);
+
+ if (F_status_is_error(status)) {
+ controller_entry_print_error(is_entry, global.main->error, cache->action, F_status_set_fine(status), "controller_entry_actions_increase_by", F_true, global.thread);
+
+ return status;
+ }
+
+ controller_entry_action_t *action = 0;
+
+ f_array_length_t allocate = 0;
+ f_array_length_t at_least = 0;
+ f_array_length_t at_most = 0;
+
+ f_array_length_t i = 0;
+ f_array_length_t j = 0;
+
+ for (; i < cache->object_actions.used; ++i) {
+
+ cache->action.line_action = 0;
+ cache->action.name_action.used = 0;
+
+ action = &actions->array[actions->used];
+ action->type = 0;
+ action->code = 0;
+ action->line = 0;
+ action->number = 0;
+ action->status = F_known_not;
+ action->parameters.used = 0;
+
+ status = f_fss_count_lines(cache->buffer_file, cache->object_actions.array[i].start, &cache->action.line_action);
+
+ if (F_status_is_error(status)) {
+ controller_entry_print_error(is_entry, global.main->error, cache->action, F_status_set_fine(status), "f_fss_count_lines", F_true, global.thread);
+ break;
+ }
+
+ action->line = ++cache->action.line_action;
+
+ status = controller_dynamic_rip_nulless_terminated(cache->buffer_file, cache->object_actions.array[i], &cache->action.name_action);
+
+ if (F_status_is_error(status)) {
+ controller_entry_print_error(is_entry, global.main->error, cache->action, F_status_set_fine(status), "controller_dynamic_rip_nulless_terminated", F_true, global.thread);
+ break;
+ }
+
+ if (fl_string_dynamic_compare_string(controller_consider_s, cache->action.name_action, controller_consider_s_length) == F_equal_to) {
+ actions->array[actions->used].type = controller_entry_action_type_consider_e;
+ }
+ else if (fl_string_dynamic_compare_string(controller_execute_s, cache->action.name_action, controller_execute_s_length) == F_equal_to) {
+ actions->array[actions->used].type = controller_entry_action_type_execute_e;
+ }
+ else if (fl_string_dynamic_compare_string(controller_failsafe_s, cache->action.name_action, controller_failsafe_s_length) == F_equal_to) {
+ actions->array[actions->used].type = controller_entry_action_type_failsafe_e;
+ }
+ else if (fl_string_dynamic_compare_string(controller_freeze_s, cache->action.name_action, controller_freeze_s_length) == F_equal_to) {
+ actions->array[actions->used].type = controller_entry_action_type_freeze_e;
+ }
+ else if (fl_string_dynamic_compare_string(controller_item_s, cache->action.name_action, controller_item_s_length) == F_equal_to) {
+ actions->array[actions->used].type = controller_entry_action_type_item_e;
+ }
+ else if (fl_string_dynamic_compare_string(controller_kill_s, cache->action.name_action, controller_kill_s_length) == F_equal_to) {
+ actions->array[actions->used].type = controller_entry_action_type_kill_e;
+ }
+ else if (fl_string_dynamic_compare_string(controller_pause_s, cache->action.name_action, controller_pause_s_length) == F_equal_to) {
+ actions->array[actions->used].type = controller_entry_action_type_pause_e;
+ }
+ else if (fl_string_dynamic_compare_string(controller_ready_s, cache->action.name_action, controller_ready_s_length) == F_equal_to) {
+ actions->array[actions->used].type = controller_entry_action_type_ready_e;
+ }
+ else if (fl_string_dynamic_compare_string(controller_reload_s, cache->action.name_action, controller_reload_s_length) == F_equal_to) {
+ actions->array[actions->used].type = controller_entry_action_type_reload_e;
+ }
+ else if (fl_string_dynamic_compare_string(controller_restart_s, cache->action.name_action, controller_restart_s_length) == F_equal_to) {
+ actions->array[actions->used].type = controller_entry_action_type_restart_e;
+ }
+ else if (fl_string_dynamic_compare_string(controller_resume_s, cache->action.name_action, controller_resume_s_length) == F_equal_to) {
+ actions->array[actions->used].type = controller_entry_action_type_resume_e;
+ }
+ else if (fl_string_dynamic_compare_string(controller_start_s, cache->action.name_action, controller_start_s_length) == F_equal_to) {
+ actions->array[actions->used].type = controller_entry_action_type_start_e;
+ }
+ else if (fl_string_dynamic_compare_string(controller_stop_s, cache->action.name_action, controller_stop_s_length) == F_equal_to) {
+ actions->array[actions->used].type = controller_entry_action_type_stop_e;
+ }
+ else if (fl_string_dynamic_compare_string(controller_thaw_s, cache->action.name_action, controller_thaw_s_length) == F_equal_to) {
+ actions->array[actions->used].type = controller_entry_action_type_thaw_e;
+ }
+ else if (fl_string_dynamic_compare_string(controller_timeout_s, cache->action.name_action, controller_timeout_s_length) == F_equal_to) {
+ actions->array[actions->used].type = controller_entry_action_type_timeout_e;
+ }
+ else {
+ if (global.main->warning.verbosity == f_console_verbosity_debug_e) {
+ controller_lock_print(global.main->warning.to, global.thread);
+
+ fl_print_format("%c%[%SUnknown %s item action '%]", global.main->warning.to.stream, f_string_eol_s[0], global.main->warning.context, global.main->warning.prefix, is_entry ? controller_entry_s : controller_exit_s, global.main->warning.context);
+ fl_print_format("%[%S%]", global.main->warning.to.stream, global.main->warning.notable, cache->action.name_action, global.main->warning.notable);
+ fl_print_format("%['.%]%c", global.main->warning.to.stream, global.main->warning.context, global.main->warning.context, f_string_eol_s[0]);
+
+ controller_entry_print_error_cache(is_entry, global.main->warning, cache->action);
+
+ controller_unlock_print_flush(global.main->warning.to, global.thread);
+ }
+
+ continue;
+ }
+
+ if (action->type == controller_entry_action_type_consider_e || controller_entry_action_type_is_rule(action->type)) {
+ allocate = cache->content_actions.array[i].used;
+ at_least = 2;
+ at_most = allocate;
+ }
+ else if (action->type == controller_entry_action_type_execute_e) {
+ allocate = cache->content_actions.array[i].used;
+ at_least = 1;
+ at_most = allocate;
+ }
+ else if (action->type == controller_entry_action_type_failsafe_e || action->type == controller_entry_action_type_item_e) {
+ allocate = 1;
+ at_least = 1;
+ at_most = 1;
+ }
+ else if (action->type == controller_entry_action_type_timeout_e) {
+ allocate = 2;
+ at_least = 2;
+ at_most = 2;
+ }
+ else if (action->type == controller_entry_action_type_ready_e) {
+ allocate = 1;
+ at_least = 0;
+ at_most = 1;
+ }
+
+ 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 (global.main->error.verbosity != f_console_verbosity_quiet_e) {
+ f_thread_mutex_lock(&global.thread->lock.print);
+
+ flockfile(global.main->error.to.stream);
+
+ fl_print_format("%c%[%SThe %s item action '%]", global.main->error.to.stream, f_string_eol_s[0], global.main->error.context, global.main->error.prefix, is_entry ? controller_entry_s : controller_exit_s, global.main->error.context);
+ fl_print_format("%[%Q%]", global.main->error.to.stream, global.main->error.notable, cache->action.name_action, global.main->error.notable);
+ fl_print_format("%[' requires ", global.main->error.to.stream, global.main->error.context);
+
+ if (at_least == at_most) {
+ f_print_terminated("exactly ", global.main->error.to.stream);
+ }
+
+ fl_print_format("%]%[%un%]", global.main->error.to.stream, global.main->error.context, global.main->error.notable, at_least, global.main->error.notable);
+
+ if (action->type == controller_entry_action_type_consider_e || controller_entry_action_type_is_rule(action->type)) {
+ fl_print_format("%[ or more parameters.%]%c", global.main->error.to.stream, global.main->error.context, global.main->error.context, f_string_eol_s[0]);
+ }
+ else {
+ if (at_least == at_most) {
+ fl_print_format("%[ parameters.%]%c", global.main->error.to.stream, global.main->error.context, global.main->error.context, f_string_eol_s[0]);
+ }
+ else {
+ fl_print_format("%[ to %]", global.main->error.to.stream, global.main->error.context, global.main->error.context);
+ fl_print_format("%[%un%]", global.main->error.to.stream, global.main->error.notable, at_most, global.main->error.notable);
+ fl_print_format("%[ parameters.%]%c", global.main->error.to.stream, global.main->error.context, global.main->error.context, f_string_eol_s[0]);
+ }
+ }
+
+ funlockfile(global.main->error.to.stream);
+
+ f_thread_mutex_unlock(&global.thread->lock.print);
+ }
+ }
+ else {
+ 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 = f_string_dynamics_increase_by(allocate, &action->parameters);
+
+ if (F_status_is_error(status)) {
+ controller_entry_print_error(is_entry, global.main->error, cache->action, F_status_set_fine(status), "f_string_dynamics_increase_by", F_true, global.thread);
+
+ action->status = status;
+
+ if (F_status_is_error_not(status_action)) {
+ status_action = status;
+ }
+
+ break;
+ }
+
+ for (j = 0; j < allocate && j < cache->content_actions.array[i].used; ++j) {
+
+ action->parameters.array[j].used = 0;
+
+ status = f_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)) {
+ controller_entry_print_error(is_entry, global.main->error, cache->action, F_status_set_fine(status), "f_string_dynamic_partial_append_nulless", F_true, global.thread);
+
+ action->status = status;
+
+ if (F_status_is_error_not(status_action)) {
+ status_action = status;
+ }
+
+ break;
+ }
+
+ ++action->parameters.used;
+ } // for
+
+ if (F_status_is_error_not(action->status)) {
+ if (action->type == controller_entry_action_type_consider_e || controller_entry_action_type_is_rule(action->type)) {
+ if (action->parameters.array[0].used) {
+
+ // Force the path to be canonical (removing all '../' parts).
+ status = fll_path_canonical(action->parameters.array[0].string, &cache->buffer_path);
+
+ if (F_status_is_error(status)) {
+ // @todo instead call: fll_error_file_print().
+ // fll_error_file_print(main->error, F_status_set_fine(status), "fll_path_canonical", F_true, arguments->argv[location], "verify", fll_error_file_type_path_e);
+ controller_entry_print_error(is_entry, global.main->error, cache->action, F_status_set_fine(status), "fll_path_canonical", F_true, global.thread);
+
+ action->status = status;
+
+ if (F_status_set_fine(status) == F_memory_not) {
+ status_action = status;
+ break;
+ }
+
+ if (F_status_is_error_not(status_action)) {
+ status_action = status;
+ }
+ }
+ }
+ else {
+ action->status = F_status_set_error(F_parameter);
+
+ if (F_status_is_error_not(status_action)) {
+ status_action = action->status;
+ }
+
+ if (global.main->error.verbosity != f_console_verbosity_quiet_e) {
+ fll_print_format("%c%[%SThe %s item action must not have an empty string for a path (the first parameter).%]%c", global.main->error.to.stream, f_string_eol_s[0], global.main->error.context, global.main->error.prefix, is_entry ? controller_entry_s : controller_exit_s, global.main->error.context, f_string_eol_s[0]);
+ }
+ }
+
+ if (action->parameters.array[1].used) {
+ cache->buffer_path.used = 0;
+
+ status = f_file_name_base(action->parameters.array[1].string, action->parameters.array[1].used, &cache->buffer_path);
+
+ if (F_status_is_error(status)) {
+ controller_entry_print_error(is_entry, global.main->error, cache->action, F_status_set_fine(status), "f_file_name_base", F_true, global.thread);
+
+ if (F_status_set_fine(status) == F_memory_not) {
+ status_action = status;
+ break;
+ }
+
+ action->status = status;
+
+ if (F_status_is_error_not(status_action)) {
+ status_action = status;
+ }
+ }
+ else {
+ if (fl_string_dynamic_compare(action->parameters.array[1], cache->buffer_path) == F_equal_to_not) {
+
+ if (global.main->error.verbosity != f_console_verbosity_quiet_e) {
+ status = f_string_dynamic_terminate_after(&cache->buffer_path);
+
+ if (F_status_is_error(status)) {
+ controller_entry_print_error(is_entry, global.main->error, cache->action, F_status_set_fine(status), "f_string_dynamic_terminate_after", F_true, global.thread);
+
+ action->status = status;
+
+ if (F_status_set_fine(status) == F_memory_not) {
+ status_action = status;
+ }
+
+ break;
+ }
+
+ flockfile(global.main->error.to.stream);
+
+ fl_print_format("%c%[%SThe %s item action second parameter '%]", global.main->error.to.stream, f_string_eol_s[0], global.main->error.context, global.main->error.prefix, is_entry ? controller_entry_s : controller_exit_s, global.main->error.context);
+ fl_print_format("%[%Q%]", global.main->error.to.stream, global.main->error.notable, action->parameters.array[1], global.main->error.notable);
+ fl_print_format("%[' must be a base path name, such as '%]", global.main->error.to.stream, global.main->error.context, global.main->error.context);
+ fl_print_format("%[%Q%]", global.main->error.to.stream, global.main->error.notable, cache->buffer_path, global.main->error.notable);
+ fl_print_format("%['.%]", global.main->error.to.stream, global.main->error.context, global.main->error.context, f_string_eol_s[0]);
+
+ funlockfile(global.main->error.to.stream);
+ }
+
+ action->status = F_status_set_error(F_parameter);
+
+ if (F_status_is_error_not(status_action)) {
+ status_action = action->status;
+ }
+ }
+ }
+ }
+ else {
+ action->status = F_status_set_error(F_parameter);
+
+ if (F_status_is_error_not(status_action)) {
+ status_action = action->status;
+ }
+
+ if (global.main->error.verbosity != f_console_verbosity_quiet_e) {
+ fll_print_format("%c%[%SThe %s item action must not have an empty string for a rule name (the second parameter).%]", global.main->error.to.stream, f_string_eol_s[0], global.main->error.context, global.main->error.prefix, is_entry ? controller_entry_s : controller_exit_s, global.main->error.context, f_string_eol_s[0]);
+ }
+ }
+
+ for (j = 2; j < action->parameters.used; ++j) {
+
+ if (fl_string_dynamic_compare_string(controller_asynchronous_s, action->parameters.array[j], controller_asynchronous_s_length) == F_equal_to) {
+ action->code |= controller_entry_rule_code_asynchronous_d;
+ }
+ else if (fl_string_dynamic_compare_string(controller_require_s, action->parameters.array[j], controller_require_s_length) == F_equal_to) {
+ action->code |= controller_entry_rule_code_require_d;
+ }
+ else if (fl_string_dynamic_compare_string(controller_wait_s, action->parameters.array[j], controller_wait_s_length) == F_equal_to) {
+ action->code |= controller_entry_rule_code_wait_d;
+ }
+ else {
+ if (action->status == F_none) {
+ action->status = F_status_set_error(F_supported_not);
+
+ if (F_status_is_error_not(status_action)) {
+ status_action = action->status;
+ }
+ }
+
+ if (global.main->error.verbosity != f_console_verbosity_quiet_e) {
+ flockfile(global.main->error.to.stream);
+
+ fl_print_format("%c%[%SThe %s item action third parameter (and beyond) must be one of '%]", global.main->error.to.stream, f_string_eol_s[0], global.main->error.context, global.main->error.prefix, is_entry ? controller_entry_s : controller_exit_s, global.main->error.context);
+ fl_print_format("%[%s%]", global.main->error.to.stream, global.main->error.notable, controller_asynchronous_s, global.main->error.notable);
+ fl_print_format("%[', '%]", global.main->error.to.stream, global.main->error.context, global.main->error.context);
+ fl_print_format("%[%s%]", global.main->error.to.stream, global.main->error.notable, controller_require_s, global.main->error.notable);
+ fl_print_format("%[', or '%]", global.main->error.to.stream, global.main->error.context, global.main->error.context);
+ fl_print_format("%[%s%]", global.main->error.to.stream, global.main->error.notable, controller_wait_s, global.main->error.notable);
+ fl_print_format("%[' but instead has '%]", global.main->error.to.stream, global.main->error.context, global.main->error.context);
+ fl_print_format("%[%Q%]", global.main->error.to.stream, global.main->error.notable, action->parameters.array[j], global.main->error.notable);
+ fl_print_format("%['.%]", global.main->error.to.stream, global.main->error.context, global.main->error.context, f_string_eol_s[0]);
+
+ funlockfile(global.main->error.to.stream);
+ }
+ }
+ } // for
+ }
+ else if (action->type == controller_entry_action_type_failsafe_e || action->type == controller_entry_action_type_item_e) {
+ if (fl_string_dynamic_compare_string(controller_main_s, action->parameters.array[0], controller_main_s_length) == F_equal_to) {
+ action->status = F_status_set_error(F_supported_not);
+
+ if (F_status_is_error_not(status_action)) {
+ status_action = action->status;
+ }
+
+ if (global.main->error.verbosity != f_console_verbosity_quiet_e) {
+ flockfile(global.main->error.to.stream);
+
+ fl_print_format("%c%[%SThe %s item action may not specify the reserved item '%]", global.main->error.to.stream, f_string_eol_s[0], global.main->error.context, global.main->error.prefix, is_entry ? controller_entry_s : controller_exit_s, global.main->error.context);
+ fl_print_format("%[%s%]", global.main->error.to.stream, global.main->error.notable, controller_main_s, global.main->error.notable);
+ fl_print_format("%['.%]", global.main->error.to.stream, global.main->error.context, global.main->error.context, f_string_eol_s[0]);
+
+ funlockfile(global.main->error.to.stream);
+ }
+ }
+ }
+ else if (action->type == controller_entry_action_type_timeout_e) {
+
+ if (fl_string_dynamic_compare_string(controller_kill_s, action->parameters.array[0], controller_kill_s_length) == F_equal_to) {
+ action->code = controller_entry_timeout_code_kill_d;
+ }
+ else if (fl_string_dynamic_compare_string(controller_start_s, action->parameters.array[0], controller_start_s_length) == F_equal_to) {
+ action->code = controller_entry_timeout_code_start_d;
+ }
+ else if (fl_string_dynamic_compare_string(controller_stop_s, action->parameters.array[0], controller_stop_s_length) == F_equal_to) {
+ action->code = controller_entry_timeout_code_stop_d;
+ }
+ else {
+ action->status = F_status_set_error(F_supported_not);
+
+ if (F_status_is_error_not(status_action)) {
+ status_action = action->status;
+ }
+
+ if (global.main->error.verbosity != f_console_verbosity_quiet_e) {
+ flockfile(global.main->error.to.stream);
+
+ fl_print_format("%c%[%SThe %s item action must have one of '%]", global.main->error.to.stream, f_string_eol_s[0], global.main->error.context, global.main->error.prefix, is_entry ? controller_entry_s : controller_exit_s, global.main->error.context);
+ fl_print_format("%[%s%]", global.main->error.to.stream, global.main->error.notable, controller_kill_s, global.main->error.notable);
+ fl_print_format("%[', '%]", global.main->error.to.stream, global.main->error.context, global.main->error.context);
+ fl_print_format("%[%s%]", global.main->error.to.stream, global.main->error.notable, controller_start_s, global.main->error.notable);
+ fl_print_format("%[', or '%]", global.main->error.to.stream, global.main->error.context, global.main->error.context);
+ fl_print_format("%[%s%]", global.main->error.to.stream, global.main->error.notable, controller_stop_s, global.main->error.notable);
+ fl_print_format("%[' but instead has '%]", global.main->error.to.stream, global.main->error.context, global.main->error.context);
+ fl_print_format("%[%Q%]", global.main->error.to.stream, global.main->error.notable, action->parameters.array[0], global.main->error.notable);
+ fl_print_format("%['.%]", global.main->error.to.stream, global.main->error.context, global.main->error.context, f_string_eol_s[0]);
+
+ funlockfile(global.main->error.to.stream);
+ }
+ }
+
+ if (action->status == F_none) {
+ const f_string_range_t range = macro_f_string_range_t_initialize(action->parameters.array[1].used);
+
+ status = fl_conversion_string_to_number_unsigned(action->parameters.array[1].string, range, &action->number);
+
+ if (F_status_is_error(status) || status == F_data_not) {
+ action->number = 0;
+
+ if (status == F_data_not) {
+ action->status = F_status_set_error(F_number);
+ }
+ else {
+ action->status = controller_status_simplify_error(F_status_set_fine(status));
+ }
+
+ if (F_status_set_fine(status) == F_memory_not) {
+ controller_entry_print_error(is_entry, global.main->error, cache->action, F_status_set_fine(status), "fl_conversion_string_to_number_unsigned", F_true, global.thread);
+
+ status_action = status;
+ break;
+ }
+
+ if (global.main->error.verbosity != f_console_verbosity_quiet_e) {
+ flockfile(global.main->error.to.stream);
+
+ fl_print_format("%c%[%SThe %s item action parameter '%]", global.main->error.to.stream, f_string_eol_s[0], global.main->error.context, global.main->error.prefix, is_entry ? controller_entry_s : controller_exit_s, global.main->error.context);
+ fl_print_format("%[%Q%]", global.main->error.to.stream, global.main->error.notable, action->parameters.array[1], global.main->error.notable);
+ fl_print_format("%[' is not a valid supported number.%]", global.main->error.to.stream, global.main->error.context, global.main->error.context, f_string_eol_s[0]);
+
+ funlockfile(global.main->error.to.stream);
+ }
+ }
+ }
+ }
+ else if (action->type == controller_entry_action_type_ready_e) {
+ if (action->parameters.used) {
+ if (fl_string_dynamic_compare_string(controller_wait_s, action->parameters.array[0], controller_wait_s_length) == F_equal_to) {
+ action->code |= controller_entry_rule_code_wait_d;
+ }
+ else {
+ action->status = F_status_set_error(F_supported_not);
+
+ if (global.main->error.verbosity != f_console_verbosity_quiet_e) {
+ flockfile(global.main->error.to.stream);
+
+ fl_print_format("%c%[%SThe %s item action may only have '%]", global.main->error.to.stream, f_string_eol_s[0], global.main->error.context, global.main->error.prefix, is_entry ? controller_entry_s : controller_exit_s, global.main->error.context);
+ fl_print_format("%[%s%]", global.main->error.to.stream, global.main->error.notable, controller_wait_s, global.main->error.notable);
+ fl_print_format("%[' but instead has '%]", global.main->error.to.stream, global.main->error.context, global.main->error.context);
+ fl_print_format("%[%Q%]", global.main->error.to.stream, global.main->error.notable, action->parameters.array[0], global.main->error.notable);
+ fl_print_format("%['.%]", global.main->error.to.stream, global.main->error.context, global.main->error.context, f_string_eol_s[0]);
+
+ funlockfile(global.main->error.to.stream);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ ++actions->used;
+ } // for
+
+ if (F_status_is_error(status_action)) {
+ return status_action;
+ }
+
+ return status;
+ }
+#endif // _di_controller_entry_actions_read_
+
+#ifndef _di_controller_entry_preprocess_
+ f_status_t controller_entry_preprocess(const controller_global_t global, const bool is_entry, controller_cache_t * const cache) {
+
+ f_status_t status = F_none;
+ f_status_t status2 = F_none;
+
+ f_array_length_t i = 0;
+ f_array_length_t j = 0;
+
+ f_array_length_t at_i = 0;
+ f_array_length_t at_j = 1;
+
+ controller_entry_t *entry = is_entry ? &global.setting->entry : &global.setting->exit;
+ controller_entry_actions_t *actions = 0;
+
+ uint8_t error_has = F_false;
+
+ // This effectively sets the read for an entry and resets the ready for an exit.
+ // @todo should there be a ready_exit instead?
+ // @todo the global.setting->ready in this function may need mutex lock protection.
+ // @todo disconnect the socket file if applicable.
+ global.setting->ready = controller_setting_ready_no_e;
+
+ cache->ats.used = 0;
+
+ cache->action.line_action = 0;
+ cache->action.line_item = 0;
+ cache->action.name_action.used = 0;
+ cache->action.name_item.used = 0;
+
+ macro_f_array_lengths_t_increase_by(status, cache->ats, controller_common_allocation_small_d)
+
+ if (F_status_is_error(status)) {
+ controller_entry_print_error(is_entry, global.main->error, cache->action, F_status_set_fine(status), "macro_f_array_lengths_t_increase_by", F_true, global.thread);
+
+ return status;
+ }
+
+ // Utilize the ats cache as an item execution stack (at_i is for item index, and at_j (at_i + 1) is for action index).
+ cache->ats.array[0] = 0;
+ cache->ats.array[1] = 0;
+ cache->ats.used = 2;
+
+ cache->action.line_item = entry->items.array[0].line;
+ cache->action.name_item.used = 0;
+
+ status = controller_dynamic_append_terminated(entry->items.array[0].name, &cache->action.name_item);
+
+ if (F_status_is_error(status)) {
+ controller_entry_print_error(is_entry, global.main->error, cache->action, F_status_set_fine(status), "controller_dynamic_append_terminated", F_true, global.thread);
+
+ return status;
+ }
+
+ while (controller_thread_is_enabled(is_entry, global.thread)) {
+
+ actions = &entry->items.array[cache->ats.array[at_i]].actions;
+
+ for (; cache->ats.array[at_j] < actions->used && controller_thread_is_enabled(is_entry, global.thread); ++cache->ats.array[at_j]) {
+
+ cache->action.line_action = actions->array[cache->ats.array[at_j]].line;
+ cache->action.name_action.used = 0;
+
+ status2 = controller_dynamic_append_terminated(controller_entry_action_type_name(actions->array[cache->ats.array[at_j]].type), &cache->action.name_action);
+
+ if (F_status_is_error(status2)) {
+ controller_entry_print_error(is_entry, global.main->error, cache->action, F_status_set_fine(status2), "controller_dynamic_append_terminated", F_true, global.thread);
+
+ return status2;
+ }
+
+ if (actions->array[cache->ats.array[at_j]].type == controller_entry_action_type_ready_e) {
+
+ if (global.setting->ready == controller_setting_ready_wait_e) {
+ if (global.main->warning.verbosity == f_console_verbosity_debug_e) {
+ controller_lock_print(global.main->warning.to, global.thread);
+
+ fl_print_format("%c%[%SMultiple '%]", global.main->warning.to.stream, f_string_eol_s[0], global.main->warning.context, global.main->warning.prefix, global.main->warning.context);
+ fl_print_format("%[%s%]", global.main->warning.to.stream, global.main->warning.notable, controller_ready_s, global.main->warning.notable);
+ fl_print_format("%[' %s item actions detected; only the first will be used.%]%c", global.main->warning.to.stream, global.main->warning.context, is_entry ? controller_entry_s : controller_exit_s, global.main->warning.context, f_string_eol_s[0]);
+
+ controller_entry_print_error_cache(is_entry, global.main->warning, cache->action);
+
+ controller_unlock_print_flush(global.main->warning.to, global.thread);
+ }
+ }
+ else {
+ global.setting->ready = controller_setting_ready_wait_e;
+ }
+ }
+ else if (actions->array[cache->ats.array[at_j]].type == controller_entry_action_type_item_e) {
+ error_has = F_false;
+
+ // "main" is not allowed to be used for an "item" and "setting" is not an executable "item".
+ if (fl_string_dynamic_compare_string(controller_main_s, actions->array[cache->ats.array[at_j]].parameters.array[0], controller_main_s_length) == F_equal_to) {
+ continue;
+ }
+ else if (fl_string_dynamic_compare_string(controller_setting_s, actions->array[cache->ats.array[at_j]].parameters.array[0], controller_setting_s_length) == F_equal_to) {
+ continue;
+ }
+
+ // Walk though each items and check to see if the item actually exists.
+ for (i = 1; i < entry->items.used && controller_thread_is_enabled(is_entry, global.thread); ++i) {
+
+ if (fl_string_dynamic_compare(entry->items.array[i].name, actions->array[cache->ats.array[at_j]].parameters.array[0]) == F_equal_to) {
+
+ // Check to see if "i" is already in the stack (to prevent recursion) (skipping main).
+ for (j = 2; j < cache->ats.used; j += 2) {
+
+ if (cache->ats.array[j] == i) {
+ if (global.main->error.verbosity != f_console_verbosity_quiet_e) {
+ controller_lock_print(global.main->error.to, global.thread);
+
+ fl_print_format("%c%[%SThe %s item named '%]", global.main->error.to.stream, f_string_eol_s[0], global.main->error.context, is_entry ? controller_entry_s : controller_exit_s, global.main->error.prefix, global.main->error.context);
+ fl_print_format("%[%Q%]", global.main->error.to.stream, global.main->error.notable, entry->items.array[i].name, global.main->error.notable);
+ fl_print_format("%[' cannot be executed because recursion is not allowed.%]%c", global.main->error.to.stream, global.main->error.context, global.main->error.context, f_string_eol_s[0]);
+
+ controller_entry_print_error_cache(is_entry, global.main->error, cache->action);
+
+ controller_unlock_print_flush(global.main->error.to, global.thread);
+ }
+
+ if (F_status_is_error_not(status)) {
+ status = F_status_set_error(F_recurse);
+ }
+
+ error_has = F_true;
+ break;
+ }
+ } // for
+
+ if (error_has) break;
+
+ macro_f_array_lengths_t_increase_by(status2, cache->ats, controller_common_allocation_small_d)
+
+ if (F_status_is_error(status2)) {
+ controller_entry_print_error(is_entry, global.main->error, cache->action, F_status_set_fine(status2), "macro_f_array_lengths_t_increase_by", F_true, global.thread);
+
+ return status2;
+ }
+
+ // Save the value so to avoid string comparison during normal operation.
+ actions->array[cache->ats.array[at_j]].number = i;
+
+ // Continue into the requested item.
+ at_i = cache->ats.used;
+ at_j = cache->ats.used + 1;
+
+ cache->ats.array[at_i] = i;
+ cache->ats.array[at_j] = 0;
+ cache->ats.used += 2;
+
+ cache->action.name_action.used = 0;
+ cache->action.line_action = 0;
+
+ cache->action.name_item.used = 0;
+ cache->action.line_item = entry->items.array[i].line;
+
+ status2 = controller_dynamic_append_terminated(entry->items.array[i].name, &cache->action.name_item);
+
+ if (F_status_is_error(status2)) {
+ controller_entry_print_error(is_entry, global.main->error, cache->action, F_status_set_fine(status2), "controller_dynamic_append_terminated", F_true, global.thread);
+
+ return status2;
+ }
+
+ break;
+ }
+ } // for
+
+ if (error_has || i >= entry->items.used) {
+ if (i >= entry->items.used) {
+ if (global.main->error.verbosity != f_console_verbosity_quiet_e) {
+ controller_lock_print(global.main->error.to, global.thread);
+
+ fl_print_format("%c%[%SThe %s item named '%]", global.main->error.to.stream, f_string_eol_s[0], global.main->error.context, is_entry ? controller_entry_s : controller_exit_s, global.main->error.prefix, global.main->error.context);
+ fl_print_format("%[%Q%]", global.main->error.to.stream, global.main->error.notable, actions->array[cache->ats.array[at_j]].parameters.array[0], global.main->error.notable);
+ fl_print_format("%[' does not exist.%]%c", global.main->error.to.stream, global.main->error.context, global.main->error.context, f_string_eol_s[0]);
+
+ controller_entry_print_error_cache(is_entry, global.main->error, cache->action);
+
+ controller_unlock_print_flush(global.main->error.to, global.thread);
+ }
+
+ if (F_status_is_error_not(status)) {
+ status = F_status_set_error(F_valid_not);
+ }
+ }
+ }
+ else {
+ break;
+ }
+ }
+ } // for
+
+ cache->action.line_action = 0;
+ cache->action.name_action.used = 0;
+
+ // End of actions found, so drop to previous loop in stack.
+ if (cache->ats.array[at_j] == actions->used) {
+
+ // All actions for "main" are processed so there is nothing left to do.
+ if (at_i == 0) break;
+
+ at_i -= 2;
+ at_j -= 2;
+
+ cache->ats.used -= 2;
+ ++cache->ats.array[at_j];
+
+ cache->action.line_item = entry->items.array[cache->ats.array[at_i]].line;
+ cache->action.name_item.used = 0;
+
+ status2 = controller_dynamic_append_terminated(entry->items.array[cache->ats.array[at_i]].name, &cache->action.name_item);
+
+ if (F_status_is_error(status2)) {
+ controller_entry_print_error(is_entry, global.main->error, cache->action, F_status_set_fine(status2), "controller_dynamic_append_terminated", F_true, global.thread);
+
+ return status2;
+ }
+ }
+ } // while
+
+ if (!controller_thread_is_enabled(is_entry, global.thread)) {
+ return F_status_set_error(F_interrupt);
+ }
+
+ // If ready was never found in the entry, then default to always ready.
+ if (global.setting->ready == controller_setting_ready_no_e) {
+ global.setting->ready = controller_setting_ready_yes_e;
+ }
+
+ return status;
+ }
+#endif // _di_controller_entry_preprocess_
+
+#ifndef _di_controller_entry_process_
+ f_status_t controller_entry_process(const controller_global_t *global, controller_cache_t * const cache, const bool failsafe, const bool is_entry) {
+
+ f_status_t status = F_none;
+ f_status_t status_lock = F_none;
+
+ f_array_length_t i = 0;
+ f_array_length_t j = 0;
+
+ f_array_length_t at_i = 0;
+ f_array_length_t at_j = 1;
+
+ uint8_t options_force = 0;
+ uint8_t options_process = 0;
+
+ controller_entry_t *entry = is_entry ? &global->setting->entry : &global->setting->exit;
+ controller_entry_action_t *entry_action = 0;
+ controller_entry_actions_t *entry_actions = 0;
+ controller_process_t *process = 0;
+
+ // an empty stack is used here because each rule here is the first rule run in the rule's scope.
+ const f_array_lengths_t stack = f_array_lengths_t_initialize;
+
+ cache->ats.used = 0;
+ cache->stack.used = 0;
+
+ cache->action.line_action = 0;
+ cache->action.line_item = 0;
+ cache->action.name_action.used = 0;
+ cache->action.name_item.used = 0;
+
+ macro_f_array_lengths_t_increase_by(status, cache->ats, controller_common_allocation_small_d)
+
+ if (F_status_is_error(status)) {
+ controller_entry_print_error(is_entry, global->main->error, cache->action, F_status_set_fine(status), "macro_f_array_lengths_t_increase_by", F_true, global->thread);
+
+ return status;
+ }
+
+ // utilize the ats cache as an item execution stack (at_i is for item index, and at_j (at_i + 1) is for action index).
+ cache->ats.array[0] = failsafe ? global->setting->failsafe_item_id : 0;
+ cache->ats.array[1] = 0;
+ cache->ats.used = 2;
+
+ cache->action.line_item = entry->items.array[cache->ats.array[0]].line;
+ cache->action.name_item.used = 0;
+
+ status = controller_dynamic_append_terminated(entry->items.array[cache->ats.array[0]].name, &cache->action.name_item);
+
+ if (F_status_is_error(status)) {
+ controller_entry_print_error(is_entry, global->main->error, cache->action, F_status_set_fine(status), "controller_dynamic_append_terminated", F_true, global->thread);
+
+ return status;
+ }
+
+ if (global->main->parameters[controller_parameter_simulate_e].result == f_console_result_found_e || global->main->error.verbosity == f_console_verbosity_verbose_e || global->main->error.verbosity == f_console_verbosity_debug_e) {
+ if (global->main->error.verbosity != f_console_verbosity_quiet_e) {
+ controller_lock_print(global->main->output.to, global->thread);
+
+ fl_print_format("%cProcessing %s%s item '", global->main->output.to.stream, f_string_eol_s[0], failsafe ? "failsafe " : "", is_entry ? controller_entry_s : controller_exit_s);
+ fl_print_format("%[%Q%]'.%c", global->main->output.to.stream, global->main->context.set.notable, cache->action.name_item, global->main->context.set.notable, f_string_eol_s[0]);
+
+ controller_unlock_print_flush(global->main->output.to, global->thread);
+ }
+ }
+
+ // The pre-process determines if ready is explicitly specified within the entry file and if it is not start as ready.
+ if (global->setting->ready == controller_setting_ready_yes_e) {
+ status = controller_perform_ready(global, cache, is_entry);
+ if (F_status_is_error(status)) return status;
+ }
+
+ while (controller_thread_is_enabled(is_entry, global->thread)) {
+
+ entry_actions = &entry->items.array[cache->ats.array[at_i]].actions;
+
+ for (; cache->ats.array[at_j] < entry_actions->used && controller_thread_is_enabled(is_entry, global->thread); ++cache->ats.array[at_j]) {
+
+ entry_action = &entry_actions->array[cache->ats.array[at_j]];
+
+ cache->action.line_action = entry_action->line;
+ cache->action.name_action.used = 0;
+
+ status = controller_dynamic_append_terminated(controller_entry_action_type_name(entry_action->type), &cache->action.name_action);
+
+ if (F_status_is_error(status)) {
+ controller_entry_print_error(is_entry, global->main->error, cache->action, F_status_set_fine(status), "controller_dynamic_append_terminated", F_true, global->thread);
+
+ return status;
+ }
+
+ if (F_status_is_error(entry_action->status)) {
+ if (global->main->parameters[controller_parameter_simulate_e].result == f_console_result_found_e) {
+ if (global->main->error.verbosity != f_console_verbosity_quiet_e) {
+ controller_lock_print(global->main->output.to, global->thread);
+
+ fl_print_format("%cThe %s item action '", global->main->output.to.stream, f_string_eol_s[0], is_entry ? controller_entry_s : controller_exit_s);
+ fl_print_format("%[%Q%]", global->main->output.to.stream, global->main->context.set.title, cache->action.name_action, global->main->context.set.title);
+
+ if (entry_action->parameters.used) {
+ fl_print_format(" %[", global->main->output.to.stream, global->main->context.set.notable);
+
+ controller_entry_action_parameters_print(global->main->output.to.stream, *entry_action);
+
+ fl_print_format("%]", global->main->output.to.stream, global->main->context.set.notable);
+ }
+
+ fl_print_format("' is %[%s%] and is in a ", global->main->output.to.stream, global->main->context.set.notable, entry_action->code & controller_entry_rule_code_require_d ? "required" : "optional", global->main->context.set.notable);
+
+ fl_print_format("%[failed%] state, skipping.%c", global->main->output.to.stream, global->main->context.set.notable, global->main->context.set.notable, global->main->context.set.notable, f_string_eol_s[0]);
+
+ controller_unlock_print_flush(global->main->output.to, global->thread);
+ }
+ }
+ else {
+ if ((entry_action->code & controller_entry_rule_code_require_d) && global->main->error.verbosity != f_console_verbosity_quiet_e || !(entry_action->code & controller_entry_rule_code_require_d) && (global->main->warning.verbosity == f_console_verbosity_verbose_e || global->main->warning.verbosity == f_console_verbosity_debug_e)) {
+ fl_print_t *output = 0;
+
+ if (entry_action->code & controller_entry_rule_code_require_d) {
+ output = &global->main->error;
+ }
+ else {
+ output = &global->main->warning;
+ }
+
+ controller_lock_print(output->to, global->thread);
+
+ fl_print_format("%c%[%SThe %s item action '%]", output->to.stream, f_string_eol_s[0], output->context, output->prefix ? output->prefix : f_string_empty_s, is_entry ? controller_entry_s : controller_exit_s, output->context);
+ fl_print_format("%[%Q%]", output->to.stream, output->notable, cache->action.name_action, output->notable);
+
+
+ if (entry_action->parameters.used) {
+ fl_print_format(" %[", output->to.stream, global->main->context.set.notable);
+
+ controller_entry_action_parameters_print(output->to.stream, *entry_action);
+
+ fl_print_format("%]", output->to.stream, global->main->context.set.notable);
+ }
+
+ if (entry_action->code & controller_entry_rule_code_require_d) {
+ fl_print_format("%[' is%] %[required%]", output->to.stream, output->context, output->context, output->notable, output->notable);
+ }
+ else {
+ fl_print_format("%[' is%] %[optional%]", output->to.stream, output->context, output->context, output->notable, output->notable);
+ }
+
+ fl_print_format(" %[and is in a%] %[failed%]", output->to.stream, output->context, output->context, output->notable, output->notable);
+
+ if (entry_action->code & controller_entry_rule_code_require_d) {
+ fl_print_format(" %[state, aborting.%]%c", output->to.stream, output->context, output->context, f_string_eol_s[0]);
+ }
+ else {
+ fl_print_format(" %[state, skipping.%]%c", output->to.stream, output->context, output->context, f_string_eol_s[0]);
+ }
+
+ controller_entry_print_error_cache(is_entry, *output, cache->action);
+
+ controller_unlock_print_flush(output->to, global->thread);
+ }
+
+ if (controller_entry_action_type_is_rule(entry_action->type) && entry_action->code & controller_entry_rule_code_require_d) {
+ return F_status_is_error(F_require);
+ }
+ }
+
+ continue;
+ }
+
+ if (entry_action->type == controller_entry_action_type_ready_e) {
+ if ((entry_action->code & controller_entry_rule_code_wait_d) || global->setting->ready == controller_setting_ready_wait_e) {
+ if (global->main->parameters[controller_parameter_simulate_e].result == f_console_result_found_e || global->main->error.verbosity == f_console_verbosity_verbose_e || global->main->error.verbosity == f_console_verbosity_debug_e || entry->show == controller_entry_show_init_e) {
+ if (global->main->output.verbosity != f_console_verbosity_quiet_e) {
+ controller_lock_print(global->main->output.to, global->thread);
+
+ fl_print_format("%cWaiting before processing %s item action '", global->main->output.to.stream, f_string_eol_s[0], is_entry ? controller_entry_s : controller_exit_s);
+ fl_print_format("%[%s%]", global->main->output.to.stream, global->main->context.set.title, controller_ready_s, global->main->context.set.title);
+ fl_print_format("'.%c", global->main->output.to.stream, f_string_eol_s[0]);
+
+ controller_unlock_print_flush(global->main->output.to, global->thread);
+ }
+ }
+
+ if (global->main->parameters[controller_parameter_validate_e].result == f_console_result_none_e) {
+ status = controller_rule_wait_all(*global, is_entry, F_false, process);
+ if (F_status_is_error(status)) return status;
+ }
+ }
+
+ if (global->setting->ready == controller_setting_ready_yes_e) {
+ if (global->main->parameters[controller_parameter_simulate_e].result == f_console_result_found_e || global->main->error.verbosity == f_console_verbosity_verbose_e || global->main->error.verbosity == f_console_verbosity_debug_e) {
+ if (global->main->output.verbosity != f_console_verbosity_quiet_e) {
+ controller_lock_print(global->main->output.to, global->thread);
+
+ fl_print_format("%cIgnoring %s item action '", global->main->output.to.stream, f_string_eol_s[0], is_entry ? controller_entry_s : controller_exit_s);
+ fl_print_format("%[%s%]", global->main->output.to.stream, global->main->context.set.title, controller_ready_s, global->main->context.set.title);
+ fl_print_format("', state already is ready.%c", global->main->output.to.stream, f_string_eol_s[0]);
+
+ controller_unlock_print_flush(global->main->output.to, global->thread);
+ }
+ }
+ }
+ else {
+ if (!failsafe && (global->main->error.verbosity == f_console_verbosity_verbose_e || entry->show == controller_entry_show_init_e) && global->main->parameters[controller_parameter_simulate_e].result == f_console_result_none_e) {
+ fl_print_format("%cState is now '%[%s%]'.%c", global->main->output.to.stream, f_string_eol_s[0], global->main->context.set.notable, controller_ready_s, global->main->context.set.notable, f_string_eol_s[0]);
+ }
+
+ status = controller_perform_ready(global, cache, is_entry);
+ if (F_status_is_error(status)) return status;
+ }
+ }
+ else if (entry_action->type == controller_entry_action_type_item_e) {
+ if (entry_action->number == 0 || entry_action->number >= entry->items.used || failsafe && entry_action->number == global->setting->failsafe_item_id) {
+
+ // This should not happen if the pre-process is working as intended, but in case it doesn't, return a critical error to prevent infinite recursion and similar errors.
+ if (global->main->error.verbosity != f_console_verbosity_quiet_e) {
+ controller_lock_print(global->main->error.to, global->thread);
+
+ fl_print_format("%c%[Invalid %s item index '%]", global->main->error.to.stream, f_string_eol_s[0], global->main->error.context, is_entry ? controller_entry_s : controller_exit_s, global->main->error.context);
+ fl_print_format("%[%un%]", global->main->error.to.stream, global->main->error.notable, entry_action->number, global->main->error.notable);
+ fl_print_format("%[' detected.%]%c", global->main->error.to.stream, global->main->error.context, global->main->error.context, f_string_eol_s[0]);
+
+ controller_entry_print_error_cache(is_entry, global->main->error, cache->action);
+
+ controller_unlock_print_flush(global->main->error.to, global->thread);
+ }
+
+ return F_status_is_error(F_critical);
+ }
+
+ macro_f_array_lengths_t_increase_by(status, cache->ats, controller_common_allocation_small_d)
+
+ if (F_status_is_error(status)) {
+ controller_entry_print_error(is_entry, global->main->error, cache->action, F_status_set_fine(status), "macro_f_array_lengths_t_increase_by", F_true, global->thread);
+
+ return status;
+ }
+
+ // continue into the requested item.
+ cache->ats.array[cache->ats.used] = entry_action->number;
+ cache->ats.array[cache->ats.used + 1] = 0;
+
+ at_i = cache->ats.used;
+ at_j = cache->ats.used + 1;
+
+ cache->ats.used += 2;
+
+ cache->action.name_action.used = 0;
+ cache->action.line_action = 0;
+
+ cache->action.name_item.used = 0;
+ cache->action.line_item = entry->items.array[cache->ats.array[at_i]].line;
+
+ status = controller_dynamic_append_terminated(entry->items.array[cache->ats.array[at_i]].name, &cache->action.name_item);
+
+ if (F_status_is_error(status)) {
+ controller_entry_print_error(is_entry, global->main->error, cache->action, F_status_set_fine(status), "controller_dynamic_append_terminated", F_true, global->thread);
+
+ return status;
+ }
+
+ if (global->main->parameters[controller_parameter_simulate_e].result == f_console_result_found_e || global->main->error.verbosity == f_console_verbosity_verbose_e || global->main->error.verbosity == f_console_verbosity_debug_e) {
+ if (global->main->output.verbosity != f_console_verbosity_quiet_e) {
+ controller_lock_print(global->main->output.to, global->thread);
+
+ fl_print_format("%cProcessing %s item '", global->main->output.to.stream, f_string_eol_s[0], is_entry ? controller_entry_s : controller_exit_s);
+ fl_print_format("%[%Q%]", global->main->output.to.stream, global->main->context.set.title, cache->action.name_item, global->main->context.set.title);
+ fl_print_format("'.%c", global->main->output.to.stream, f_string_eol_s[0]);
+
+ controller_unlock_print_flush(global->main->output.to, global->thread);
+ }
+ }
+
+ // Exit inner loop to force restarting and start processing the requested item.
+ break;
+ }
+ else if (entry_action->type == controller_entry_action_type_consider_e || controller_entry_action_type_is_rule(entry_action->type)) {
+ status_lock = controller_lock_write(is_entry, global->thread, &global->thread->lock.rule);
+
+ if (F_status_is_error(status_lock)) {
+ controller_lock_print_error_critical(global->main->error, F_status_set_fine(status_lock), F_false, global->thread);
+
+ break;
+ }
+
+ status = controller_rules_increase(&global->setting->rules);
+
+ f_thread_unlock(&global->thread->lock.rule);
+
+ if (F_status_is_error(status)) {
+ controller_entry_print_error(is_entry, global->main->error, cache->action, F_status_set_fine(status), "controller_rules_increase", F_true, global->thread);
+
+ return status;
+ }
+
+ const f_array_length_t id_rule_length = entry_action->parameters.array[0].used + entry_action->parameters.array[1].used + 1;
+ char id_rule_name[id_rule_length + 1];
+ const f_string_static_t alias_rule = macro_f_string_static_t_initialize(id_rule_name, id_rule_length);
+
+ memcpy(id_rule_name, entry_action->parameters.array[0].string, entry_action->parameters.array[0].used);
+ memcpy(id_rule_name + entry_action->parameters.array[0].used + 1, entry_action->parameters.array[1].string, entry_action->parameters.array[1].used);
+
+ id_rule_name[entry_action->parameters.array[0].used] = f_path_separator_s[0];
+ id_rule_name[id_rule_length] = 0;
+
+ status_lock = controller_lock_read(is_entry, global->thread, &global->thread->lock.rule);
+
+ if (F_status_is_error(status_lock)) {
+ controller_lock_print_error_critical(global->main->error, F_status_set_fine(status_lock), F_true, global->thread);
+
+ break;
+ }
+
+ status = controller_rule_find(alias_rule, global->setting->rules, 0);
+
+ f_thread_unlock(&global->thread->lock.rule);
+
+ if (global->main->parameters[controller_parameter_simulate_e].result == f_console_result_found_e || global->main->error.verbosity == f_console_verbosity_verbose_e || global->main->error.verbosity == f_console_verbosity_debug_e || (entry->show == controller_entry_show_init_e && entry_action->type != controller_entry_action_type_consider_e)) {
+ if (global->main->output.verbosity != f_console_verbosity_quiet_e) {
+ controller_lock_print(global->main->output.to, global->thread);
+
+ fl_print_format("%c%s %s item rule ", global->main->output.to.stream, f_string_eol_s[0], entry_action->type == controller_entry_action_type_consider_e ? "Considering" : "Processing", is_entry ? controller_entry_s : controller_exit_s);
+ fl_print_format("'%[%Q%]'", global->main->output.to.stream, global->main->context.set.title, alias_rule, global->main->context.set.title);
+
+ if (entry->show == controller_entry_show_init_e && global->main->parameters[controller_parameter_simulate_e].result == f_console_result_none_e) {
+ fl_print_format(" [%[%s%]]", global->main->output.to.stream, global->main->context.set.notable, entry_action->code == controller_entry_rule_code_asynchronous_d ? controller_asynchronous_s : controller_synchronous_s, global->main->context.set.notable);
+
+ if (entry_action->code == controller_entry_rule_code_wait_d) {
+ fl_print_format(" [%[%s%]]", global->main->output.to.stream, global->main->context.set.notable, controller_wait_s, global->main->context.set.notable);
+ }
+
+ if (entry_action->code == controller_entry_rule_code_require_d) {
+ fl_print_format(" [%[%s%]]", global->main->output.to.stream, global->main->context.set.notable, controller_required_s, global->main->context.set.notable);
+ }
+ }
+
+ fl_print_format(".%c", global->main->output.to.stream, f_string_eol_s[0]);
+
+ controller_unlock_print_flush(global->main->output.to, global->thread);
+ }
+ }
+
+ if (!controller_thread_is_enabled(is_entry, global->thread)) break;
+
+ // The rule is not yet loaded, ensure that it is loaded.
+ if (status != F_true) {
+
+ // Rule execution will re-use the existing cache, so save the current cache.
+ const f_array_length_t cache_line_action = cache->action.line_action;
+ const f_array_length_t cache_line_item = cache->action.line_item;
+
+ const f_array_length_t cache_name_action_used = cache->action.name_action.used;
+ const f_array_length_t cache_name_item_used = cache->action.name_item.used;
+ const f_array_length_t cache_name_file_used = cache->action.name_file.used;
+
+ char cache_name_action[cache_name_action_used];
+ char cache_name_item[cache_name_item_used];
+ char cache_name_file[cache_name_file_used];
+
+ memcpy(cache_name_action, cache->action.name_action.string, cache->action.name_action.used);
+ memcpy(cache_name_item, cache->action.name_item.string, cache->action.name_item.used);
+ memcpy(cache_name_file, cache->action.name_file.string, cache->action.name_file.used);
+
+ status_lock = controller_lock_write(is_entry, global->thread, &global->thread->lock.rule);
+
+ if (F_status_is_fine(status_lock)) {
+ status = controller_rule_read(*global, is_entry, alias_rule, cache, entry, &global->setting->rules.array[global->setting->rules.used]);
+ }
+
+ // Restore cache.
+ memcpy(cache->action.name_action.string, cache_name_action, cache_name_action_used);
+ memcpy(cache->action.name_item.string, cache_name_item, cache_name_item_used);
+ memcpy(cache->action.name_file.string, cache_name_file, cache_name_file_used);
+
+ cache->action.name_action.string[cache_name_action_used] = 0;
+ cache->action.name_item.string[cache_name_item_used] = 0;
+ cache->action.name_file.string[cache_name_file_used] = 0;
+
+ cache->action.name_action.used = cache_name_action_used;
+ cache->action.name_item.used = cache_name_item_used;
+ cache->action.name_file.used = cache_name_file_used;
+
+ cache->action.line_action = cache_line_action;
+ cache->action.line_item = cache_line_item;
+
+ if (F_status_is_error(status_lock)) {
+ controller_lock_print_error_critical(global->main->error, F_status_set_fine(status_lock), F_false, global->thread);
+ break;
+ }
+
+ if (F_status_set_fine(status) == F_interrupt || !controller_thread_is_enabled(is_entry, global->thread)) {
+ f_thread_unlock(&global->thread->lock.rule);
+
+ break;
+ }
+
+ if (F_status_is_error(status)) {
+ if (global->main->error.verbosity != f_console_verbosity_quiet_e) {
+ controller_lock_print(global->main->error.to, global->thread);
+
+ controller_entry_print_error_cache(is_entry, global->main->error, cache->action);
+
+ controller_unlock_print_flush(global->main->error.to, global->thread);
+ }
+
+ // Designate the action as failed.
+ entry_action->status = F_status_set_error(F_failure);
+
+ if (global->main->parameters[controller_parameter_simulate_e].result == f_console_result_none_e) {
+ f_thread_unlock(&global->thread->lock.rule);
+
+ if (entry_action->code & controller_entry_rule_code_require_d) {
+ return F_status_set_error(F_require);
+ }
+
+ ++cache->ats.array[at_j];
+ break;
+ }
+ }
+ else {
+ ++global->setting->rules.used;
+ }
+
+ f_thread_unlock(&global->thread->lock.rule);
+ }
+
+ if (F_status_is_error_not(status)) {
+ options_force = 0;
+ options_process = 0;
+
+ if (global->main->parameters[controller_parameter_simulate_e].result == f_console_result_found_e) {
+ options_process |= controller_process_option_simulate_d;
+ }
+
+ if (entry_action->code & controller_entry_rule_code_require_d) {
+ options_process |= controller_process_option_require_d;
+ }
+
+ if (entry_action->code & controller_entry_rule_code_wait_d) {
+ options_process |= controller_process_option_wait_d;
+ }
+
+ if (global->main->parameters[controller_parameter_validate_e].result == f_console_result_found_e) {
+ options_process |= controller_process_option_validate_d;
+ }
+
+ if (entry_action->code & controller_entry_rule_code_asynchronous_d) {
+ if (global->main->parameters[controller_parameter_validate_e].result == f_console_result_none_e) {
+ options_force |= controller_process_option_asynchronous_d;
+ }
+
+ options_process |= controller_process_option_asynchronous_d;
+ }
+
+ status = controller_rule_process_begin(*global, options_force, alias_rule, controller_entry_action_type_to_rule_action_type(entry_action->type), options_process, is_entry ? controller_process_type_entry_e : controller_process_type_exit_e, stack, *cache);
+
+ if (F_status_set_fine(status) == F_memory_not || status == F_child || F_status_set_fine(status) == F_interrupt) {
+ break;
+ }
+
+ if (F_status_is_error(status) && global->main->parameters[controller_parameter_simulate_e].result == f_console_result_none_e && (entry_action->code & controller_entry_rule_code_require_d)) {
+ return F_status_set_error(F_require);
+ }
+ }
+ }
+ else if (entry_action->type == controller_entry_action_type_execute_e) {
+ if (global->main->parameters[controller_parameter_simulate_e].result == f_console_result_found_e || global->main->error.verbosity == f_console_verbosity_verbose_e || global->main->error.verbosity == f_console_verbosity_debug_e || entry->show == controller_entry_show_init_e) {
+ if (global->main->output.verbosity != f_console_verbosity_quiet_e) {
+ controller_lock_print(global->main->output.to, global->thread);
+
+ fl_print_format("%c%s is executing '", global->main->output.to.stream, f_string_eol_s[0], is_entry ? controller_entry_s : controller_exit_s);
+
+ for (f_array_length_t k = 0; k < entry_action->parameters.used; ++k) {
+
+ fl_print_format("%[%Q%]", global->main->output.to.stream, global->main->context.set.title, entry_action->parameters.array[k], global->main->context.set.title);
+
+ if (k + 1 < entry_action->parameters.used) {
+ f_print_character(f_string_space_s[0], global->main->output.to.stream);
+ }
+ } // for
+
+ fl_print_format("'.%c", global->main->output.to.stream, f_string_eol_s[0]);
+
+ controller_unlock_print_flush(global->main->output.to, global->thread);
+ }
+ }
+
+ if (global->main->parameters[controller_parameter_simulate_e].result == f_console_result_found_e) {
+ return F_execute;
+ }
+
+ controller_thread_process_cancel(*global, is_entry, is_entry ? controller_thread_cancel_execute_e : controller_thread_cancel_exit_execute_e, process);
+
+ int result = 0;
+ int option = FL_execute_parameter_option_path_d;
+
+ if (entry->session == controller_entry_session_new_e) {
+ option |= FL_execute_parameter_option_session_d;
+ }
+
+ status = fll_execute_into(0, entry_action->parameters, option, 0, (void *) &result);
+
+ if (F_status_is_error(status)) {
+ if (F_status_set_fine(status) == F_file_found_not) {
+ if (global->main->error.verbosity != f_console_verbosity_quiet_e) {
+ controller_lock_print(global->main->error.to, global->thread);
+
+ fl_print_format("%c%[%SExecution failed, unable to find program or script '%]", global->main->error.to.stream, f_string_eol_s[0], global->main->error.context, global->main->error.prefix ? global->main->error.prefix : f_string_empty_s, global->main->error.context);
+ fl_print_format("%[%Q%]", global->main->error.to.stream, global->main->error.notable, entry_action->parameters.array[0], global->main->error.notable);
+ fl_print_format("%['.%]%c", global->main->error.to.stream, global->main->error.context, global->main->error.context, f_string_eol_s[0]);
+
+ controller_entry_print_error_cache(is_entry, global->main->error, cache->action);
+
+ controller_unlock_print_flush(global->main->error.to, global->thread);
+ }
+ }
+ else {
+ controller_entry_print_error(is_entry, global->main->error, cache->action, F_status_set_fine(status), "fll_execute_into", F_true, global->thread);
+ }
+
+ return F_status_set_error(F_execute);
+ }
+ else if (result != 0) {
+ if (global->main->error.verbosity != f_console_verbosity_quiet_e) {
+ controller_lock_print(global->main->error.to, global->thread);
+
+ fl_print_format("%c%[%SExecution failed with return value of '%]", global->main->error.to.stream, f_string_eol_s[0], global->main->error.context, global->main->error.prefix ? global->main->error.prefix : f_string_empty_s, global->main->error.context);
+ fl_print_format("%[%i%]", global->main->error.to.stream, global->main->error.notable, result, global->main->error.notable);
+ fl_print_format("$['.%]%c", global->main->error.to.stream, global->main->error.context, global->main->error.context, f_string_eol_s[0]);
+
+ controller_entry_print_error_cache(is_entry, global->main->error, cache->action);
+
+ controller_unlock_print_flush(global->main->error.to, global->thread);
+ }
+
+ return F_status_set_error(F_execute);
+ }
+
+ return F_execute;
+ }
+ else if (entry_action->type == controller_entry_action_type_timeout_e) {
+ const f_string_t suffix = " MegaTime (milliseconds)";
+
+ if (entry_action->code == controller_entry_timeout_code_kill_d) {
+ entry->timeout_kill = entry_action->number;
+
+ controller_entry_preprocess_print_simulate_setting_value(*global, is_entry, controller_timeout_s, controller_kill_s, entry->items.array[global->setting->failsafe_item_id].name, suffix);
+ }
+ else if (entry_action->code == controller_entry_timeout_code_start_d) {
+ entry->timeout_start = entry_action->number;
+
+ controller_entry_preprocess_print_simulate_setting_value(*global, is_entry, controller_timeout_s, controller_start_s, entry->items.array[global->setting->failsafe_item_id].name, suffix);
+ }
+ else if (entry_action->code == controller_entry_timeout_code_stop_d) {
+ entry->timeout_stop = entry_action->number;
+
+ controller_entry_preprocess_print_simulate_setting_value(*global, is_entry, controller_timeout_s, controller_stop_s, entry->items.array[global->setting->failsafe_item_id].name, suffix);
+ }
+ }
+ else if (entry_action->type == controller_entry_action_type_failsafe_e) {
+
+ if (failsafe) {
+ if (global->main->warning.verbosity == f_console_verbosity_debug_e) {
+ controller_lock_print(global->main->warning.to, global->thread);
+
+ fl_print_format("%c%[%SFailsafe may not be specified when running in failsafe, ignoring.%]%c", global->main->warning.to.stream, f_string_eol_s[0], global->main->warning.context, global->main->warning.prefix, global->main->warning.context, f_string_eol_s[0]);
+
+ controller_entry_print_error_cache(is_entry, global->main->warning, cache->action);
+
+ controller_unlock_print_flush(global->main->warning.to, global->thread);
+ }
+ }
+ else {
+ if (entry_action->number == 0 || entry_action->number >= entry->items.used) {
+
+ // This should not happen if the pre-process is working as designed, but in case it doesn't, return a critical error to prevent infinite recursion and similar errors.
+ if (global->main->error.verbosity != f_console_verbosity_quiet_e) {
+ controller_lock_print(global->main->error.to, global->thread);
+
+ fl_print_format("%c%[%SInvalid %s item index '%]", global->main->error.to.stream, f_string_eol_s[0], global->main->error.context, global->main->error.prefix ? global->main->error.prefix : f_string_empty_s, is_entry ? controller_entry_s : controller_exit_s, global->main->error.context);
+ fl_print_format("%[%un%]", global->main->error.to.stream, global->main->error.notable, entry_action->number, global->main->error.notable);
+ fl_print_format("%[' detected.%]%c", global->main->error.to.stream, global->main->error.context, global->main->error.context, f_string_eol_s[0]);
+
+ controller_entry_print_error_cache(is_entry, global->main->error, cache->action);
+
+ controller_unlock_print_flush(global->main->error.to, global->thread);
+ }
+
+ return F_status_is_error(F_critical);
+ }
+ else {
+ global->setting->failsafe_enabled = F_true;
+ global->setting->failsafe_item_id = entry_action->number;
+
+ controller_entry_preprocess_print_simulate_setting_value(*global, is_entry, controller_failsafe_s, 0, entry->items.array[global->setting->failsafe_item_id].name, 0);
+ }
+ }
+ }
+ } // for
+
+ if (status == F_child || F_status_set_fine(status) == F_interrupt) break;
+
+ cache->action.line_action = 0;
+ cache->action.name_action.used = 0;
+
+ if (F_status_is_error(status)) {
+ if (F_status_set_fine(status) == F_memory_not || F_status_set_fine(status) == F_require) {
+ break;
+ }
+ }
+
+ // End of actions found, so drop to previous loop in stack.
+ if (cache->ats.array[at_j] == entry_actions->used) {
+
+ // All actions for "main" are processed so there is nothing left to do.
+ if (at_i == 0) break;
+
+ at_i -= 2;
+ at_j -= 2;
+
+ cache->ats.used -= 2;
+ ++cache->ats.array[at_j];
+
+ cache->action.line_item = entry->items.array[cache->ats.array[at_i]].line;
+ cache->action.name_item.used = 0;
+
+ status = controller_dynamic_append_terminated(entry->items.array[cache->ats.array[at_i]].name, &cache->action.name_item);
+
+ if (F_status_is_error(status)) {
+ controller_entry_print_error(is_entry, global->main->error, cache->action, F_status_set_fine(status), "controller_dynamic_append_terminated", F_true, global->thread);
+
+ break;
+ }
+ }
+ } // while
+
+ if (!controller_thread_is_enabled(is_entry, global->thread)) {
+ return F_status_set_error(F_interrupt);
+ }
+
+ if (status == F_child) {
+ return status;
+ }
+
+ if (F_status_is_error(status_lock)) {
+ return status_lock;
+ }
+
+ // Check to see if any required processes failed, but do not do this if already operating in failsafe.
+ if (F_status_is_error_not(status) && !failsafe && global->main->parameters[controller_parameter_validate_e].result == f_console_result_none_e) {
+ const f_status_t status_wait = controller_rule_wait_all(*global, is_entry, F_true, 0);
+
+ if (F_status_is_error(status_wait)) {
+ return status_wait;
+ }
+
+ if (status_wait == F_require) {
+ return F_status_set_error(F_require);
+ }
+ }
+
+ if ((global->main->parameters[controller_parameter_simulate_e].result == f_console_result_found_e && global->main->error.verbosity != f_console_verbosity_quiet_e) || global->main->error.verbosity == f_console_verbosity_verbose_e) {
+ controller_lock_print(global->main->output.to, global->thread);
+
+ fl_print_format("%cDone processing %s item '", global->main->output.to.stream, f_string_eol_s[0], is_entry ? controller_entry_s : controller_exit_s);
+ fl_print_format("%[%s%]", global->main->output.to.stream, global->main->context.set.title, controller_main_s, global->main->context.set.title);
+ fl_print_format("'.%c", global->main->output.to.stream, f_string_eol_s[0]);
+
+ // failsafe should not print the extra newline because the failure exit from controller_main should handle this.
+ if (!failsafe) {
+ f_print_terminated(f_string_eol_s, global->main->output.to.stream);
+ }
+
+ controller_unlock_print_flush(global->main->output.to, global->thread);
+ }
+
+ return status;
+ }
+#endif // _di_controller_entry_process_
+
+#ifndef _di_controller_entry_read_
+ f_status_t controller_entry_read(const controller_global_t global, const bool is_entry, controller_cache_t * const cache) {
+
+ f_status_t status = F_none;
+
+ controller_entry_t *entry = is_entry ? &global.setting->entry : &global.setting->exit;
+
+ entry->status = F_known_not;
+ entry->items.used = 0;
+
+ if (global.main->as_init) {
+ entry->session = controller_entry_session_new_e;
+ }
+ else {
+ entry->session = controller_entry_session_same_e;
+ }
+
+ cache->action.line_action = 0;
+ cache->action.line_item = 0;
+
+ macro_f_time_spec_t_clear(cache->timestamp);
+
+ cache->comments.used = 0;
+ cache->delimits.used = 0;
+
+ cache->content_action.used = 0;
+
+ {
+ f_array_length_t i = 0;
+
+ for (; i < cache->content_actions.used; ++i) {
+ cache->content_actions.array[i].used = 0;
+ } // for
+
+ for (i = 0; i < cache->content_items.used; ++i) {
+ cache->content_items.array[i].used = 0;
+ } // for
+ }
+
+ 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_path.used = 0;
+
+ cache->action.name_file.used = 0;
+ cache->action.name_action.used = 0;
+ cache->action.name_item.used = 0;
+
+ if (is_entry) {
+ status = controller_file_load(global, F_true, controller_entries_s, global.setting->name_entry, controller_entry_s, controller_entries_s_length, controller_entry_s_length, cache);
+ }
+ else {
+ status = controller_file_load(global, F_false, controller_exits_s, global.setting->name_entry, controller_exit_s, controller_exits_s_length, controller_exit_s_length, cache);
+
+ if (status == F_file_found_not) {
+ return F_file_found_not;
+ }
+ }
+
+ if (F_status_is_error_not(status)) {
+ if (cache->buffer_file.used) {
+ controller_state_interrupt_t custom = macro_controller_state_interrupt_t_initialize(is_entry, global.thread);
+ f_state_t state = macro_f_state_t_initialize(controller_common_allocation_large_d, controller_common_allocation_small_d, 0, &controller_thread_signal_state_fss, 0, (void *) &custom, 0);
+ f_string_range_t range = macro_f_string_range_t_initialize(cache->buffer_file.used);
+
+ status = fll_fss_basic_list_read(cache->buffer_file, state, &range, &cache->object_items, &cache->content_items, &cache->delimits, 0, &cache->comments);
+
+ if (F_status_is_error(status)) {
+ controller_print_error(global.thread, global.main->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)) {
+ controller_entry_print_error(is_entry, global.main->error, cache->action, F_status_set_fine(status), "fl_fss_apply_delimit", F_true, global.thread);
+ }
+ }
+ }
+ else {
+ if (global.main->error.verbosity != f_console_verbosity_quiet_e) {
+ controller_lock_print(global.main->error.to, global.thread);
+
+ fll_print_format("%c%[%SThe %s file is empty.%]%c", global.main->error.to.stream, f_string_eol_s[0], global.main->error.context, global.main->error.prefix, is_entry ? controller_entry_s : is_entry ? controller_entry_s : controller_exit_s, global.main->error.context, f_string_eol_s[0]);
+
+ controller_unlock_print_flush(global.main->error.to, global.thread);
+ }
+
+ status = F_status_set_error(F_data_not);
+ }
+ }
+
+ if (F_status_is_error_not(status) && cache->object_items.used) {
+ status = controller_entry_items_increase_by(cache->object_items.used, &entry->items);
+
+ if (F_status_is_error(status)) {
+ controller_entry_print_error(is_entry, global.main->error, cache->action, F_status_set_fine(status), "controller_entry_items_increase_by", F_true, global.thread);
+ }
+ else {
+
+ // 0x1 = main found, 0x2 = found existing.
+ uint8_t code = 0;
+
+ f_string_range_t *range = 0;
+
+ f_array_length_t at = 0;
+ f_array_length_t i = 0;
+ f_array_length_t j = 0;
+
+ for (; i < cache->object_items.used && controller_thread_is_enabled(is_entry, global.thread); ++i) {
+
+ if (code & 0x2) {
+ code -= 0x2;
+ }
+
+ at = 0;
+ range = 0;
+
+ cache->action.line_action = 0;
+ cache->action.line_item = 0;
+
+ 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_path.used = 0;
+
+ cache->action.name_action.used = 0;
+ cache->action.name_item.used = 0;
+
+ status = controller_entry_items_increase_by(controller_common_allocation_small_d, &entry->items);
+
+ if (F_status_is_error(status)) {
+ controller_entry_print_error(is_entry, global.main->error, cache->action, F_status_set_fine(status), "controller_entry_items_increase_by", F_true, global.thread);
+ break;
+ }
+
+ status = controller_dynamic_partial_append_terminated(cache->buffer_file, cache->object_items.array[i], &cache->action.name_item);
+
+ if (F_status_is_error(status)) {
+ controller_entry_print_error(is_entry, global.main->error, cache->action, F_status_set_fine(status), "controller_dynamic_partial_append_terminated", F_true, global.thread);
+ break;
+ }
+
+ status = f_fss_count_lines(cache->buffer_file, cache->object_items.array[i].start, &cache->action.line_item);
+
+ if (F_status_is_error(status)) {
+ controller_entry_print_error(is_entry, global.main->error, cache->action, F_status_set_fine(status), "f_fss_count_lines", F_true, global.thread);
+ break;
+ }
+
+ ++cache->action.line_item;
+
+ for (j = (code & 0x1) ? 1 : 0; j < entry->items.used; ++j) {
+
+ if (fl_string_dynamic_compare(entry->items.array[j].name, cache->action.name_item) == F_equal_to) {
+ if (global.main->warning.verbosity == f_console_verbosity_debug_e) {
+ controller_lock_print(global.main->warning.to, global.thread);
+
+ fl_print_format("%c%[%SIgnoring duplicate %s item '%]", global.main->warning.to.stream, f_string_eol_s[0], global.main->warning.context, global.main->warning.prefix, is_entry ? controller_entry_s : controller_exit_s, global.main->warning.context);
+ fl_print_format("%[%Q%]", global.main->warning.to.stream, global.main->warning.notable, cache->action.name_file, global.main->warning.notable);
+ fl_print_format("%['.%]%c", global.main->warning.to.stream, global.main->warning.context, global.main->warning.context, f_string_eol_s[0]);
+
+ controller_entry_print_error_cache(is_entry, global.main->warning, cache->action);
+
+ controller_unlock_print_flush(global.main->warning.to, global.thread);
+ }
+
+ code |= 0x2;
+ break;
+ }
+ } // for
+
+ if (code & 0x2) continue;
+
+ range = &cache->content_items.array[i].array[0];
+
+ if (fl_string_dynamic_compare_string(controller_main_s, cache->action.name_item, controller_main_s_length) == F_equal_to) {
+ code |= 0x1;
+
+ at = 0;
+
+ if (!entry->items.used) {
+ entry->items.used = 1;
+ }
+ }
+ else if (fl_string_dynamic_compare_string(controller_setting_s, cache->action.name_item, controller_setting_s_length) == F_equal_to) {
+ status = controller_entry_settings_read(global, is_entry, *range, cache);
+
+ continue;
+ }
+ else if (entry->items.used) {
+ at = entry->items.used++;
+ }
+ else {
+
+ // skip position 0, which is reserved for "main".
+ entry->items.array[0].name.used = 0;
+
+ at = 1;
+ entry->items.used = 2;
+ }
+
+ entry->items.array[at].line = cache->action.line_item;
+
+ status = controller_dynamic_append_terminated(cache->action.name_item, &entry->items.array[at].name);
+
+ if (F_status_is_error(status)) {
+ controller_print_error(global.thread, global.main->error, F_status_set_fine(status), "controller_dynamic_append_terminated", F_true);
+
+ break;
+ }
+
+ status = controller_entry_actions_read(global, is_entry, *range, cache, &entry->items.array[at].actions);
+
+ if (F_status_is_error(status)) {
+ if (F_status_set_fine(status) != F_interrupt) {
+ controller_lock_print(global.main->error.to, global.thread);
+
+ controller_entry_print_error_cache(is_entry, global.main->error, cache->action);
+
+ controller_unlock_print_flush(global.main->error.to, global.thread);
+ }
+
+ if (F_status_set_fine(status) == F_memory_not) {
+ break;
+ }
+ }
+ } // for
+
+ if (is_entry && F_status_set_fine(status) == F_interrupt) {
+ return status;
+ }
+
+ if (F_status_is_error_not(status)) {
+ cache->action.name_action.used = 0;
+ cache->action.name_item.used = 0;
+
+ if (!(code & 0x1)) {
+ if (global.main->error.verbosity != f_console_verbosity_quiet_e) {
+ controller_lock_print(global.main->error.to, global.thread);
+
+ fl_print_format("%c%[%SThe required %s item '%]", global.main->error.to.stream, f_string_eol_s[0], global.main->error.context, global.main->error.prefix, is_entry ? controller_entry_s : controller_exit_s, global.main->error.context);
+ fl_print_format("%[%s%]", global.main->error.to.stream, global.main->error.notable, controller_main_s, global.main->error.notable);
+ fl_print_format("%[' was not found.%]%c", global.main->error.to.stream, global.main->error.context, global.main->error.context, f_string_eol_s[0]);
+
+ controller_unlock_print_flush(global.main->error.to, global.thread);
+ }
+
+ status = F_status_set_error(F_found_not);
+ }
+
+ if (F_status_is_error_not(status)) {
+ controller_entry_action_t *action = 0;
+
+ f_array_length_t k = 0;
+
+ // 0x1 = missing or not, 0x2 = one or more missing.
+ uint8_t missing = 0;
+
+ for (i = 0; i < entry->items.used; ++i) {
+
+ for (j = 0; j < entry->items.array[i].actions.used; ++j) {
+
+ if (!controller_thread_is_enabled(is_entry, global.thread)) {
+ return F_status_set_error(F_interrupt);
+ }
+
+ action = &entry->items.array[i].actions.array[j];
+
+ // only process actions that don't already have an error.
+ if (F_status_is_error(action->status)) continue;
+
+ if (action->type == controller_entry_action_type_failsafe_e || action->type == controller_entry_action_type_item_e) {
+ missing |= 0x1;
+
+ for (k = 0; k < entry->items.used; ++k) {
+
+ if (fl_string_dynamic_compare(action->parameters.array[0], entry->items.array[k].name) == F_equal_to) {
+ if (missing & 0x1) {
+ missing -= 0x1;
+ }
+
+ break;
+ }
+ } // for
+
+ if (missing & 0x1) {
+ missing |= 0x2;
+
+ cache->action.line_action = action->line;
+ cache->action.line_item = entry->items.array[i].line;
+
+ status = controller_dynamic_append_terminated(entry->items.array[i].name, &cache->action.name_item);
+
+ if (F_status_is_error(status)) {
+ controller_print_error(global.thread, global.main->error, F_status_set_fine(status), "controller_dynamic_append_terminated", F_true);
+
+ break;
+ }
+
+ if (global.main->error.verbosity != f_console_verbosity_quiet_e) {
+ controller_lock_print(global.main->error.to, global.thread);
+
+ fl_print_format("%c%[%SThe required %s item '%]", global.main->error.to.stream, f_string_eol_s[0], global.main->error.context, global.main->error.prefix, is_entry ? controller_entry_s : controller_exit_s, global.main->error.context);
+ fl_print_format("%[%Q%]", global.main->error.to.stream, global.main->error.notable, action->parameters.array[0], global.main->error.notable);
+ fl_print_format("%[' does not exist.%]%c", global.main->error.to.stream, global.main->error.context, global.main->error.context, f_string_eol_s[0]);
+
+ controller_entry_print_error_cache(is_entry, global.main->error, cache->action);
+
+ controller_unlock_print_flush(global.main->error.to, global.thread);
+ }
+
+ action->number = 0;
+ action->status = controller_status_simplify_error(F_found_not);
+
+ cache->action.name_action.used = 0;
+ cache->action.name_item.used = 0;
+ }
+ else {
+ action->number = k;
+ }
+ }
+ } // for
+ } // for
+ }
+ }
+ }
+ }
+
+ if (F_status_is_error(status)) {
+ if (F_status_set_fine(status) != F_interrupt) {
+ controller_entry_print_error_cache(is_entry, global.main->error, cache->action);
+ }
+
+ entry->status = controller_status_simplify_error(F_status_set_fine(status));
+ }
+ else {
+ entry->status = F_none;
+ }
+
+ return entry->status;
+ }
+#endif // _di_controller_entry_read_
+
+#ifndef _di_controller_entry_settings_read_
+ f_status_t controller_entry_settings_read(const controller_global_t global, const bool is_entry, const f_string_range_t content_range, controller_cache_t * const cache) {
+
+ f_status_t status = F_none;
+
+ {
+ controller_state_interrupt_t custom = macro_controller_state_interrupt_t_initialize(is_entry, global.thread);
+ f_state_t state = macro_f_state_t_initialize(controller_common_allocation_large_d, controller_common_allocation_small_d, 0, &controller_thread_signal_state_fss, 0, (void *) &custom, 0);
+ f_string_range_t range = content_range;
+
+ status = fll_fss_extended_read(cache->buffer_file, state, &range, &cache->object_actions, &cache->content_actions, 0, 0, &cache->delimits, 0);
+ }
+
+ if (F_status_is_error(status)) {
+ controller_entry_print_error(is_entry, global.main->error, cache->action, F_status_set_fine(status), "fll_fss_extended_read", F_true, global.thread);
+
+ return status;
+ }
+
+ status = fl_fss_apply_delimit(cache->delimits, &cache->buffer_file);
+
+ if (F_status_is_error(status)) {
+ controller_entry_print_error(is_entry, global.main->error, cache->action, F_status_set_fine(status), "fl_fss_apply_delimit", F_true, global.thread);
+
+ return status;
+ }
+
+ cache->delimits.used = 0;
+
+ f_array_length_t i = 0;
+ f_array_length_t j = 0;
+ f_array_length_t line = 0;
+
+ controller_entry_t *entry = is_entry ? &global.setting->entry : &global.setting->exit;
+
+ for (; i < cache->object_actions.used; ++i) {
+
+ status = f_fss_count_lines(cache->buffer_file, cache->object_actions.array[i].start, &cache->action.line_action);
+
+ if (F_status_is_error(status)) {
+ controller_entry_print_error(is_entry, global.main->error, cache->action, F_status_set_fine(status), "f_fss_count_lines", F_true, global.thread);
+
+ break;
+ }
+
+ line = ++cache->action.line_action;
+ cache->action.name_action.used = 0;
+
+ status = controller_dynamic_rip_nulless_terminated(cache->buffer_file, cache->object_actions.array[i], &cache->action.name_action);
+
+ if (F_status_is_error(status)) {
+ controller_entry_print_error(is_entry, global.main->error, cache->action, F_status_set_fine(status), "controller_dynamic_rip_nulless_terminated", F_true, global.thread);
+
+ break;
+ }
+
+ if (is_entry && fl_string_dynamic_compare_string(controller_control_s, cache->action.name_action, controller_control_s_length) == F_equal_to) {
+ if (cache->content_actions.array[i].used != 1) {
+ controller_entry_settings_read_print_setting_requires_exactly(global, is_entry, *cache, 1);
+
+ continue;
+ }
+
+ cache->action.generic.used = 0;
+ global.setting->path_control.used = 0;
+
+ status = controller_dynamic_rip_nulless_terminated(cache->buffer_file, cache->content_actions.array[i].array[0], &global.setting->path_control);
+
+ if (F_status_is_error(status)) {
+ controller_entry_print_error(is_entry, global.main->error, cache->action, F_status_set_fine(status), "controller_dynamic_rip_nulless_terminated", F_true, global.thread);
+
+ global.setting->path_control.used = 0;
+
+ break;
+ }
+
+ if (f_path_is_relative(global.setting->path_control.string, global.setting->path_control.used) == F_true) {
+
+ // Use the PID file path for creating a relative path to the control socket.
+ status = f_file_name_directory(global.setting->path_pid.string, global.setting->path_pid.used, &cache->action.generic);
+
+ if (F_status_is_error(status)) {
+ controller_entry_print_error(is_entry, global.main->error, cache->action, F_status_set_fine(status), "f_file_name_directory", F_true, global.thread);
+
+ global.setting->path_control.used = 0;
+
+ break;
+ }
+
+ status = f_string_append(f_path_separator_s, 1, &cache->action.generic);
+
+ if (F_status_is_error(status)) {
+ controller_entry_print_error(is_entry, global.main->error, cache->action, F_status_set_fine(status), "f_string_dynamic_append", F_true, global.thread);
+
+ global.setting->path_control.used = 0;
+
+ break;
+ }
+
+ status = f_string_dynamic_append(global.setting->path_control, &cache->action.generic);
+
+ if (F_status_is_error(status)) {
+ controller_entry_print_error(is_entry, global.main->error, cache->action, F_status_set_fine(status), "f_string_dynamic_append", F_true, global.thread);
+
+ global.setting->path_control.used = 0;
+
+ break;
+ }
+ }
+ else {
+ status = f_string_dynamic_append(global.setting->path_control, &cache->action.generic);
+
+ if (F_status_is_error(status)) {
+ controller_entry_print_error(is_entry, global.main->error, cache->action, F_status_set_fine(status), "f_string_dynamic_append", F_true, global.thread);
+
+ global.setting->path_control.used = 0;
+
+ break;
+ }
+ }
+
+ status = f_string_dynamic_terminate_after(&cache->action.generic);
+
+ if (F_status_is_error(status)) {
+ controller_entry_print_error(is_entry, global.main->error, cache->action, F_status_set_fine(status), "f_string_dynamic_terminate_after", F_true, global.thread);
+
+ global.setting->path_control.used = 0;
+
+ break;
+ }
+
+ status = fll_path_canonical(cache->action.generic.string, &global.setting->path_control);
+
+ if (F_status_is_error(status)) {
+ controller_entry_print_error_file(is_entry, global.main->error, cache->action, F_status_set_fine(status), "fll_path_canonical", F_true, cache->action.generic.string, "analyze", fll_error_file_type_path_e, global.thread);
+
+ global.setting->path_control.used = 0;
+
+ continue;
+ }
+ }
+ else if (is_entry && fl_string_dynamic_compare_string(controller_control_group_s, cache->action.name_action, controller_control_group_s_length) == F_equal_to) {
+ gid_t number = 0;
+
+ status = controller_get_id_group(cache->buffer_file, cache->content_actions.array[i].array[0], cache, &number);
+
+ if (F_status_is_error(status)) {
+ status = F_status_set_fine(status);
+
+ if (status == F_exist_not) {
+ controller_entry_setting_read_print_error_with_range(is_entry, global.main->error, " has an invalid group", cache->content_actions.array[i].array[0], ", because no group was found by that name", global.thread, cache);
+ }
+ else if (status == F_number_too_large) {
+ controller_entry_setting_read_print_error_with_range(is_entry, global.main->error, " has an invalid group", cache->content_actions.array[i].array[0], ", because the given ID is too large", global.thread, cache);
+ }
+ else if (status == F_number) {
+ controller_entry_setting_read_print_error_with_range(is_entry, global.main->error, " has an invalid group", cache->content_actions.array[i].array[0], ", because the given ID is not a valid supported number", global.thread, cache);
+ }
+ else {
+ controller_entry_print_error(is_entry, global.main->error, cache->action, F_status_set_fine(status), "controller_get_id_group", F_true, global.thread);
+ }
+
+ continue;
+ }
+
+ global.setting->control_group = number;
+ }
+ else if (is_entry && fl_string_dynamic_compare_string(controller_control_mode_s, cache->action.name_action, controller_control_mode_s_length) == F_equal_to) {
+ mode_t mode = 0;
+ uint8_t replace = 0;
+ f_file_mode_t mode_file = f_file_mode_t_initialize;
+
+ cache->action.generic.used = 0;
+
+ status = controller_dynamic_rip_nulless_terminated(cache->buffer_file, cache->content_actions.array[i].array[0], &cache->action.generic);
+
+ if (F_status_is_error(status)) {
+ controller_entry_print_error(is_entry, global.main->error, cache->action, F_status_set_fine(status), "controller_dynamic_rip_nulless_terminated", F_true, global.thread);
+
+ break;
+ }
+
+ status = f_file_mode_from_string(cache->action.generic.string, global.main->umask, &mode_file, &replace);
+
+ if (F_status_is_error(status)) {
+ controller_entry_setting_read_print_error_with_range(is_entry, global.main->error, " has an unsupported mode", cache->content_actions.array[i].array[0], ", because the format is unknown or contains invalid data", global.thread, cache);
+
+ continue;
+ }
+
+ status = f_file_mode_to_mode(mode_file, &mode);
+
+ if (F_status_is_error(status)) {
+ controller_entry_print_error(is_entry, global.main->error, cache->action, F_status_set_fine(status), "f_file_mode_to_mode", F_true, global.thread);
+
+ continue;
+ }
+
+ global.setting->control_mode = mode;
+ }
+ else if (is_entry && fl_string_dynamic_compare_string(controller_control_user_s, cache->action.name_action, controller_control_user_s_length) == F_equal_to) {
+ uid_t number = 0;
+
+ status = controller_get_id_user(cache->buffer_file, cache->content_actions.array[i].array[0], cache, &number);
+
+ if (F_status_is_error(status)) {
+ status = F_status_set_fine(status);
+
+ if (status == F_exist_not) {
+ controller_entry_setting_read_print_error_with_range(is_entry, global.main->error, " has an invalid user", cache->content_actions.array[i].array[0], ", because no user was found by that name", global.thread, cache);
+ }
+ else if (status == F_number_too_large) {
+ controller_entry_setting_read_print_error_with_range(is_entry, global.main->error, " has an invalid user", cache->content_actions.array[i].array[0], ", because the given ID is too large", global.thread, cache);
+ }
+ else if (status == F_number) {
+ controller_entry_setting_read_print_error_with_range(is_entry, global.main->error, " has an invalid user", cache->content_actions.array[i].array[0], ", because the given ID is not a valid supported number", global.thread, cache);
+ }
+ else {
+ controller_entry_print_error(is_entry, global.main->error, cache->action, F_status_set_fine(status), "controller_get_id_user", F_true, global.thread);
+ }
+
+ continue;
+ }
+
+ global.setting->control_user = number;
+ }
+ else if (is_entry && fl_string_dynamic_compare_string(controller_mode_s, cache->action.name_action, controller_mode_s_length) == F_equal_to) {
+ if (cache->content_actions.array[i].used != 1) {
+ controller_entry_settings_read_print_setting_requires_exactly(global, is_entry, *cache, 1);
+
+ continue;
+ }
+
+ if (fl_string_dynamic_partial_compare_string(controller_service_s, cache->buffer_file, controller_service_s_length, cache->content_actions.array[i].array[0]) == F_equal_to) {
+ global.setting->mode = controller_setting_mode_service_e;
+ }
+ else if (fl_string_dynamic_partial_compare_string(controller_program_s, cache->buffer_file, controller_program_s_length, cache->content_actions.array[i].array[0]) == F_equal_to) {
+ global.setting->mode = controller_setting_mode_program_e;
+ }
+ else {
+ controller_entry_settings_read_print_setting_unknown_action_value(global, is_entry, *cache, i);
+
+ continue;
+ }
+ }
+ else if (fl_string_dynamic_compare_string(controller_pid_s, cache->action.name_action, controller_pid_s_length) == F_equal_to) {
+ if (cache->content_actions.array[i].used != 1) {
+ controller_entry_settings_read_print_setting_requires_exactly(global, is_entry, *cache, 1);
+
+ continue;
+ }
+
+ if (fl_string_dynamic_partial_compare_string(controller_disable_s, cache->buffer_file, controller_disable_s_length, cache->content_actions.array[i].array[0]) == F_equal_to) {
+ entry->pid = controller_entry_pid_disable_e;
+ }
+ else if (fl_string_dynamic_partial_compare_string(controller_ready_s, cache->buffer_file, controller_ready_s_length, cache->content_actions.array[i].array[0]) == F_equal_to) {
+ entry->pid = controller_entry_pid_ready_e;
+ }
+ else if (fl_string_dynamic_partial_compare_string(controller_require_s, cache->buffer_file, controller_require_s_length, cache->content_actions.array[i].array[0]) == F_equal_to) {
+ entry->pid = controller_entry_pid_require_e;
+ }
+ else {
+ controller_entry_settings_read_print_setting_unknown_action_value(global, is_entry, *cache, i);
+
+ continue;
+ }
+ }
+ else if (fl_string_dynamic_compare_string(controller_session_s, cache->action.name_action, controller_session_s_length) == F_equal_to) {
+ if (cache->content_actions.array[i].used != 1) {
+ controller_entry_settings_read_print_setting_requires_exactly(global, is_entry, *cache, 1);
+
+ continue;
+ }
+
+ if (fl_string_dynamic_partial_compare_string(controller_new_s, cache->buffer_file, controller_new_s_length, cache->content_actions.array[i].array[0]) == F_equal_to) {
+ entry->session = controller_entry_session_new_e;
+ }
+ else if (fl_string_dynamic_partial_compare_string(controller_same_s, cache->buffer_file, controller_same_s_length, cache->content_actions.array[i].array[0]) == F_equal_to) {
+ entry->session = controller_entry_session_same_e;
+ }
+ else {
+ controller_entry_settings_read_print_setting_unknown_action_value(global, is_entry, *cache, i);
+
+ continue;
+ }
+ }
+ else if (fl_string_dynamic_compare_string(controller_show_s, cache->action.name_action, controller_show_s_length) == F_equal_to) {
+ if (cache->content_actions.array[i].used != 1) {
+ controller_entry_settings_read_print_setting_requires_exactly(global, is_entry, *cache, 1);
+
+ continue;
+ }
+
+ if (fl_string_dynamic_partial_compare_string(controller_normal_s, cache->buffer_file, controller_normal_s_length, cache->content_actions.array[i].array[0]) == F_equal_to) {
+ entry->show = controller_entry_show_normal_e;
+ }
+ else if (fl_string_dynamic_partial_compare_string(controller_init_s, cache->buffer_file, controller_init_s_length, cache->content_actions.array[i].array[0]) == F_equal_to) {
+ entry->show = controller_entry_show_init_e;
+ }
+ else {
+ controller_entry_settings_read_print_setting_unknown_action_value(global, is_entry, *cache, i);
+
+ continue;
+ }
+ }
+ else {
+ if (global.main->warning.verbosity == f_console_verbosity_debug_e) {
+ controller_entry_settings_read_print_setting_unknown_action(global, is_entry, *cache);
+ }
+
+ continue;
+ }
+ } // for
+
+ return status;
+ }
+#endif // _di_controller_entry_settings_read_
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
--- /dev/null
+/**
+ * FLL - Level 3
+ *
+ * Project: Controller
+ * API Version: 0.5
+ * Licenses: lgpl-2.1-or-later
+ */
+#ifndef _PRIVATE_entry_h
+#define _PRIVATE_entry_h
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Determine if the type code represents a Rule type.
+ *
+ * @param type
+ * The type code to compare against.
+ *
+ * @return
+ * TRUE if Rule type.
+ * FALSE otherwise.
+ */
+#ifndef _di_controller_entry_action_type_is_rule_
+ extern f_status_t controller_entry_action_type_is_rule(uint8_t type) F_attribute_visibility_internal_d;
+#endif // _di_controller_entry_action_type_is_rule_
+
+/**
+ * Get a string representing the entry action type.
+ *
+ * @param type
+ * The entry action type code.
+ *
+ * @return
+ * The string with used > 0 on success.
+ * The string with used == 0 if no match was found.
+ */
+#ifndef _di_controller_entry_action_type_name_
+ extern f_string_static_t controller_entry_action_type_name(const uint8_t type) F_attribute_visibility_internal_d;
+#endif // _di_controller_entry_action_type_name_
+
+/**
+ * Convert the Entry Action type to Rule Action type.
+ *
+ * @param type
+ * The Entry Action type.
+ *
+ * @return
+ * TRUE if Rule type.
+ * FALSE otherwise.
+ */
+#ifndef _di_controller_entry_action_type_to_rule_action_type_
+ extern uint8_t controller_entry_action_type_to_rule_action_type(uint8_t type) F_attribute_visibility_internal_d;
+#endif // _di_controller_entry_action_type_to_rule_action_type_
+
+/**
+ * Read the entry list, extracting all items and values.
+ *
+ * @param global
+ * The global data.
+ * @param is_entry
+ * If TRUE, then this loads as an entry.
+ * If FALSE, then this loads as an exit.
+ * @param content_range
+ * The range in the list buffer representing the content.
+ * @param cache
+ * A structure for containing and caching relevant data.
+ * @param actions
+ * The processed actions.
+ *
+ * @return
+ * F_none on success.
+ *
+ * Errors (with error bit) from: controller_entry_actions_increase_by().
+ * Errors (with error bit) from: f_fss_count_lines().
+ * Errors (with error bit) from: fl_fss_apply_delimit().
+ * Errors (with error bit) from: f_string_dynamic_partial_append_nulless().
+ * Errors (with error bit) from: fl_string_dynamic_rip_nulless().
+ * Errors (with error bit) from: f_string_dynamic_terminate_after().
+ * Errors (with error bit) from: f_string_dynamics_increase_by().
+ * Errors (with error bit) from: fll_fss_extended_read().
+ *
+ * @see controller_entry_actions_increase_by()
+ * @see f_fss_count_lines()
+ * @see f_string_dynamic_partial_append_nulless()
+ * @see f_string_dynamic_terminate_after()
+ * @see f_string_dynamics_increase_by()
+ * @see fl_fss_apply_delimit()
+ * @see fl_string_dynamic_rip_nulless()
+ * @see fll_fss_extended_read()
+ */
+#ifndef _di_controller_entry_actions_read_
+ extern f_status_t controller_entry_actions_read(const controller_global_t global, const bool is_entry, const f_string_range_t content_range, controller_cache_t * const cache, controller_entry_actions_t *actions) F_attribute_visibility_internal_d;
+#endif // _di_controller_entry_actions_read_
+
+/**
+ * Pre-process all items for the loaded entry.
+ *
+ * @param global
+ * The global data.
+ * @param is_entry
+ * If TRUE, then this operate as an entry.
+ * If FALSE, then this operate as an exit.
+ * @param cache
+ * The main/global cache to use.
+ *
+ * @return
+ * F_none on success.
+ * F_recurse (with error bit) on a recursion error.
+ * F_valid_not (with error bit) on invalid entry item, entry item action, or entry item action value.
+ *
+ * Errors (with error bit) from: macro_f_array_lengths_t_increase_by().
+ * Errors (with error bit) from: f_string_dynamic_append().
+ * Errors (with error bit) from: f_string_dynamic_terminate_after().
+ *
+ * This will detect and report all errors, but only the first error is returned.
+ * Memory related errors return immediately.
+
+ * @see macro_f_array_lengths_t_increase_by()
+ * @see f_string_dynamic_append()
+ * @see f_string_dynamic_terminate_after()
+ */
+#ifndef _di_controller_entry_preprocess_
+ extern f_status_t controller_entry_preprocess(const controller_global_t global, const bool is_entry, controller_cache_t * const cache) F_attribute_visibility_internal_d;
+#endif // _di_controller_entry_preprocess_
+
+/**
+ * Process (execute) all Items for the loaded Entry or Exit.
+ *
+ * @param global
+ * The global data.
+ * @param cache
+ * The main/global cache to use.
+ * @param failsafe
+ * If TRUE, operate in failsafe mode (starts at designated failsafe Item).
+ * If FALSE, operate in normal mode (starts at "main" Item).
+ * @param is_entry
+ * If TRUE, then this operate as an entry.
+ * If FALSE, then this operate as an exit.
+ *
+ * @return
+ * F_none on success.
+ * F_execute on success and program exiting (scripts may result in this) or when execute would have been executed but is instead simulated.
+ *
+ * F_require (with error bit) if a required Item failed.
+ * F_critical (with error bit) on any critical error.
+ * F_execute (with error bit) if the "execute" Item Action failed.
+ *
+ * Errors (with error bit) from: macro_f_array_lengths_t_increase_by().
+ * Errors (with error bit) from: controller_perform_ready().
+ * Errors (with error bit) from: controller_dynamic_append_terminated().
+ *
+ * @see macro_f_array_lengths_t_increase_by()
+ * @see controller_perform_ready()
+ * @see controller_dynamic_append_terminated()
+ */
+#ifndef _di_controller_entry_process_
+ extern f_status_t controller_entry_process(const controller_global_t *global, controller_cache_t * const cache, const bool failsafe, const bool is_entry) F_attribute_visibility_internal_d;
+#endif // _di_controller_entry_process_
+
+/**
+ * Read the entry, extracting all lists.
+ *
+ * @param global
+ * The global data.
+ * @param is_entry
+ * If TRUE, then this loads as an entry.
+ * If FALSE, then this loads as an exit.
+ * @param cache
+ * The cache for the specific thread.
+ * This should be the cache global.thread->asynchronouss.array[global.id].cache.
+ *
+ * @return
+ * F_none on success.
+ * F_file_found_not on file not found for a an exit file (is_entry is FALSE).
+ *
+ * Errors (with error bit) from: controller_entry_actions_read().
+ * Errors (with error bit) from: controller_entry_items_increase_by().
+ * Errors (with error bit) from: controller_file_load().
+ * Errors (with error bit) from: controller_status_simplify_error().
+ * Errors (with error bit) from: controller_dynamic_append_terminated().
+ * Errors (with error bit) from: controller_dynamic_partial_append_terminated().
+ * Errors (with error bit) from: f_fss_count_lines().
+ * Errors (with error bit) from: fl_fss_apply_delimit().
+ * Errors (with error bit) from: f_string_dynamic_append().
+ * Errors (with error bit) from: f_string_dynamic_partial_append_nulless().
+ * Errors (with error bit) from: f_string_dynamic_terminate().
+ * Errors (with error bit) from: fll_fss_basic_list_read().
+ *
+ * @see controller_entry_actions_read()
+ * @see controller_entry_items_increase_by()
+ * @see controller_file_load()
+ * @see controller_status_simplify_error()
+ * @see controller_dynamic_append_terminated()
+ * @see controller_dynamic_partial_append_terminated()
+ * @see f_fss_count_lines()
+ * @see fl_fss_apply_delimit()
+ * @see f_string_dynamic_append()
+ * @see f_string_dynamic_partial_append_nulless()
+ * @see f_string_dynamic_terminate()
+ * @see fll_fss_basic_list_read()
+ */
+#ifndef _di_controller_entry_read_
+ extern f_status_t controller_entry_read(const controller_global_t global, const bool is_entry, controller_cache_t * const cache) F_attribute_visibility_internal_d;
+#endif // _di_controller_entry_read_
+
+/**
+ * Read the entry settings, loading all settings.
+ *
+ * @param global
+ * The global data.
+ * @param is_entry
+ * If TRUE, then this loads as an entry.
+ * If FALSE, then this loads as an exit.
+ * @param content_range
+ * The range in the list buffer representing the content.
+ * @param cache
+ * A structure for containing and caching relevant data.
+ */
+#ifndef _di_controller_entry_settings_read_
+ extern f_status_t controller_entry_settings_read(const controller_global_t global, const bool is_entry, const f_string_range_t content_range, controller_cache_t * const cache) F_attribute_visibility_internal_d;
+#endif // _di_controller_entry_settings_read_
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // _PRIVATE_entry_h
--- /dev/null
+#include "../controller/controller.h"
+#include "../common/private-common.h"
+#include "../entry/private-entry_print.h"
+#include "../lock/private-lock_print.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef _di_controller_entry_action_parameters_print_
+ void controller_entry_action_parameters_print(FILE * const stream, const controller_entry_action_t action) {
+
+ for (f_array_length_t index = 0; ;) {
+
+ f_print_dynamic_safely(action.parameters.array[index], stream);
+
+ ++index;
+
+ if (index == action.parameters.used) break;
+
+ f_print_terminated(f_string_space_s, stream);
+ } // for
+ }
+#endif // _di_controller_entry_action_parameters_print_
+
+#ifndef _di_controller_entry_preprocess_print_simulate_setting_value_
+ void controller_entry_preprocess_print_simulate_setting_value(const controller_global_t global, const bool is_entry, const f_string_t name, const f_string_t name_sub, const f_string_static_t value, const f_string_t suffix) {
+
+ if (global.main->error.verbosity != f_console_verbosity_debug_e && !(global.main->error.verbosity == f_console_verbosity_verbose_e && global.main->parameters[controller_parameter_simulate_e].result == f_console_result_found_e)) {
+ return;
+ }
+
+ controller_lock_print(global.main->output.to, global.thread);
+
+ fl_print_format("%cProcessing %s item action '", global.main->output.to.stream, f_string_eol_s[0], is_entry ? controller_entry_s : controller_exit_s);
+
+ fl_print_format("%[%S%]' setting ", global.main->output.to.stream, global.main->context.set.title, name, global.main->context.set.title);
+
+ if (name_sub) {
+ fl_print_format("'%[%S%]'", global.main->output.to.stream, global.main->context.set.notable, name_sub, global.main->context.set.notable);
+ }
+ else {
+ fl_print_format("value", global.main->output.to.stream);
+ }
+
+ fl_print_format(" to '%[%Q%]", global.main->output.to.stream, global.main->context.set.important, value, global.main->context.set.important);
+
+ fl_print_format("'%S.%c", global.main->output.to.stream, suffix, f_string_eol_s[0]);
+
+ controller_unlock_print_flush(global.main->output.to, global.thread);
+ }
+#endif // _di_controller_entry_preprocess_print_simulate_setting_value_
+
+#ifndef _di_controller_entry_print_error_
+ void controller_entry_print_error(const bool is_entry, const fl_print_t print, const controller_cache_action_t cache, const f_status_t status, const f_string_t function, const bool fallback, controller_thread_t *thread) {
+
+ if (print.verbosity == f_console_verbosity_quiet_e) return;
+ if (status == F_interrupt) return;
+
+ // fll_error_print() automatically locks, so manually handle only the mutex locking and flushing rather than calling controller_lock_print().
+ f_thread_mutex_lock(&thread->lock.print);
+
+ fll_error_print(print, status, function, fallback);
+
+ flockfile(print.to.stream);
+
+ controller_entry_print_error_cache(is_entry, print, cache);
+
+ controller_unlock_print_flush(print.to, thread);
+ }
+#endif // _di_controller_entry_print_error_
+
+#ifndef _di_controller_entry_print_error_cache_
+ void controller_entry_print_error_cache(const bool is_entry, const fl_print_t output, const controller_cache_action_t cache) {
+
+ fl_print_format("%c%[%SWhile processing ", output.to.stream, f_string_eol_s[0], output.context, output.prefix);
+
+ if (cache.name_action.used) {
+ fl_print_format("action '%]", output.to.stream, output.context);
+ fl_print_format("%[%Q%]", output.to.stream, output.notable, cache.name_action, output.notable);
+ fl_print_format("%[' on line%] ", output.to.stream, output.context, output.context);
+ fl_print_format("%[%un%]", output.to.stream, output.notable, cache.line_action, output.notable);
+ fl_print_format("%[ for ", output.to.stream, output.context);
+ }
+
+ if (cache.name_item.used) {
+ fl_print_format("%s item '%]", output.to.stream, is_entry ? controller_entry_s : controller_exit_s, output.context);
+ fl_print_format("%[%Q%]", output.to.stream, output.notable, cache.name_item, output.notable);
+ fl_print_format("%[' on line%] ", output.to.stream, output.context, output.context);
+ fl_print_format("%[%un%]", output.to.stream, output.notable, cache.line_item, output.notable);
+ fl_print_format("%[ for ", output.to.stream, output.context);
+ }
+
+ if (cache.name_file.used) {
+ fl_print_format("%s file '%]", output.to.stream, is_entry ? controller_entry_s : controller_exit_s, output.context);
+ fl_print_format("%[%Q%]%['", output.to.stream, output.notable, cache.name_file, output.notable, output.context);
+ }
+
+ fl_print_format(".%]%c", output.to.stream, output.context, f_string_eol_s[0]);
+ }
+#endif // _di_controller_entry_print_error_cache_
+
+#ifndef _di_controller_entry_print_error_file_
+ void controller_entry_print_error_file(const bool is_entry, const fl_print_t print, const controller_cache_action_t cache, const f_status_t status, const f_string_t function, const bool fallback, const f_string_t name, const f_string_t operation, const uint8_t type, controller_thread_t *thread) {
+
+ if (print.verbosity == f_console_verbosity_quiet_e) return;
+ if (status == F_interrupt) return;
+
+ // fll_error_file_print() automatically locks, so manually handle only the mutex locking and flushing rather than calling controller_lock_print().
+ f_thread_mutex_lock(&thread->lock.print);
+
+ fll_error_file_print(print, status, function, fallback, name, operation, type);
+
+ flockfile(print.to.stream);
+
+ controller_entry_print_error_cache(is_entry, print, cache);
+
+ controller_unlock_print_flush(print.to, thread);
+ }
+#endif // _di_controller_entry_print_error_file_
+
+#ifndef _di_controller_entry_setting_read_print_error_with_range_
+ void controller_entry_setting_read_print_error_with_range(const bool is_entry, const fl_print_t print, const f_string_t before, const f_string_range_t range, const f_string_t after, controller_thread_t * const thread, controller_cache_t * const cache) {
+
+ if (print.verbosity == f_console_verbosity_quiet_e) return;
+
+ controller_lock_print(print.to, thread);
+
+ fl_print_format("%c%[%S%s setting%S '%]", print.to.stream, f_string_eol_s[0], print.context, print.prefix, is_entry ? "Entry" : "Exit", before, print.context);
+ fl_print_format("%[%/Q%]", print.to.stream, print.notable, cache->buffer_file, range, print.notable);
+ fl_print_format("%['%S.%]%c", print.to.stream, print.context, after, print.context, f_string_eol_s[0]);
+
+ controller_entry_print_error_cache(is_entry, print, cache->action);
+
+ controller_unlock_print_flush(print.to, thread);
+ }
+#endif // _di_controller_entry_setting_read_print_error_with_range_
+
+#ifndef _di_controller_entry_settings_read_print_setting_requires_exactly_
+ void controller_entry_settings_read_print_setting_requires_exactly(const controller_global_t global, const bool is_entry, const controller_cache_t cache, const f_number_unsigned_t total) {
+
+ if (global.main->error.verbosity == f_console_verbosity_quiet_e) return;
+
+ controller_lock_print(global.main->error.to, global.thread);
+
+ fl_print_format("%c%[%SThe %s item setting '%]", global.main->error.to.stream, f_string_eol_s[0], global.main->error.context, global.main->error.prefix, is_entry ? controller_entry_s : controller_exit_s, global.main->error.context);
+ fl_print_format("%[%Q%]", global.main->error.to.stream, global.main->error.notable, cache.action.name_action, global.main->error.notable);
+ fl_print_format("%[' requires exactly %]", global.main->error.to.stream, global.main->error.context, global.main->error.context);
+ fl_print_format("%[%un%]", global.main->error.to.stream, global.main->error.notable, total, global.main->error.notable);
+ fl_print_format("%[' %s.%]%c", global.main->error.to.stream, global.main->error.context, total > 1 ? controller_parameters_s : controller_parameter_s, global.main->error.context, f_string_eol_s[0]);
+
+ controller_entry_print_error_cache(is_entry, global.main->error, cache.action);
+
+ controller_unlock_print_flush(global.main->error.to, global.thread);
+ }
+#endif // _di_controller_entry_settings_read_print_setting_requires_exactly_
+
+#ifndef _di_controller_entry_settings_read_print_setting_unknown_action_
+ void controller_entry_settings_read_print_setting_unknown_action(const controller_global_t global, const bool is_entry, const controller_cache_t cache) {
+
+ if (global.main->warning.verbosity != f_console_verbosity_debug_e) return;
+
+ controller_lock_print(global.main->warning.to, global.thread);
+
+ fl_print_format("%c%[%SUnknown %s item setting '%]", global.main->warning.to.stream, f_string_eol_s[0], global.main->warning.context, global.main->warning.prefix, is_entry ? controller_entry_s : controller_exit_s, global.main->warning.context);
+ fl_print_format("%[%Q%]", global.main->warning.to.stream, global.main->warning.notable, cache.action.name_action, global.main->warning.notable);
+ fl_print_format("%['.%]%c", global.main->warning.to.stream, global.main->warning.context, global.main->warning.context, f_string_eol_s[0]);
+
+ controller_entry_print_error_cache(is_entry, global.main->warning, cache.action);
+
+ controller_unlock_print_flush(global.main->warning.to, global.thread);
+ }
+#endif // _di_controller_entry_settings_read_print_setting_unknown_action_
+
+#ifndef _di_controller_entry_settings_read_print_setting_unknown_action_value_
+ void controller_entry_settings_read_print_setting_unknown_action_value(const controller_global_t global, const bool is_entry, const controller_cache_t cache, const f_array_length_t index) {
+
+ if (global.main->warning.verbosity != f_console_verbosity_debug_e) return;
+
+ controller_lock_print(global.main->warning.to, global.thread);
+
+ fl_print_format("%c%[%SThe %s item setting '%]", global.main->warning.to.stream, f_string_eol_s[0], global.main->warning.context, global.main->warning.prefix, is_entry ? controller_entry_s : controller_exit_s, global.main->warning.context);
+ fl_print_format("%[%Q%]", global.main->warning.to.stream, global.main->warning.notable, cache.action.name_action, global.main->warning.notable);
+ fl_print_format("%[' has an unknown value '%]", global.main->warning.to.stream, f_string_eol_s[0], global.main->warning.context, global.main->warning.prefix, is_entry ? controller_entry_s : controller_exit_s, global.main->warning.context);
+ fl_print_format("%[%Q%]", global.main->warning.to.stream, global.main->warning.notable, cache.content_actions.array[index].array[0], global.main->warning.notable);
+ fl_print_format("%['.%]%c", global.main->warning.to.stream, global.main->warning.context, global.main->warning.context, f_string_eol_s[0]);
+
+ controller_entry_print_error_cache(is_entry, global.main->warning, cache.action);
+
+ controller_unlock_print_flush(global.main->warning.to, global.thread);
+ }
+#endif // _di_controller_entry_settings_read_print_setting_unknown_action_value_
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
--- /dev/null
+/**
+ * FLL - Level 3
+ *
+ * Project: Controller
+ * API Version: 0.5
+ * Licenses: lgpl-2.1-or-later
+ */
+#ifndef _PRIVATE_entry_print_h
+#define _PRIVATE_entry_print_h
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Print all parameters for some action, separated by a space.
+ *
+ * @param stream
+ * The file stream to print to.
+ * @param action
+ * The entry action whose parameters will be printed.
+ */
+#ifndef _di_controller_entry_action_parameters_print_
+ extern void controller_entry_action_parameters_print(FILE * const stream, const controller_entry_action_t action) F_attribute_visibility_internal_d;
+#endif // _di_controller_entry_action_parameters_print_
+
+/**
+ * Print message regarding the population of a setting when in simulation or verbose mode.
+ *
+ * @param global
+ * The global data.
+ * @param is_entry
+ * If TRUE, then this operate as an entry.
+ * If FALSE, then this operate as an exit.
+ * @param name
+ * The Object name of the setting being populated.
+ * @param name_sub
+ * (optional) A sub-name associated with the setting being populated.
+ * Set to NULL to disable.
+ * @param value
+ * The value being set.
+ * @param suffix
+ * An additional message to append at the end (before the final period).
+ */
+#ifndef _di_controller_entry_preprocess_print_simulate_setting_value_
+ extern void controller_entry_preprocess_print_simulate_setting_value(const controller_global_t global, const bool is_entry, const f_string_t name, const f_string_t name_sub, const f_string_static_t value, const f_string_t suffix) F_attribute_visibility_internal_d;
+#endif // _di_controller_entry_preprocess_print_simulate_setting_value_
+
+/**
+ * Print the entry related error, locking the print mutex during the print.
+ *
+ * @param is_entry
+ * If TRUE, then this loads as an entry.
+ * If FALSE, then this loads as an exit.
+ * @param print
+ * Designates how printing is to be performed.
+ * @param cache
+ * The action cache.
+ * @param status
+ * The status code to process.
+ * Make sure this has F_status_set_fine() called if the status code has any error or warning bits.
+ * @param function
+ * The name of the function where the error happened.
+ * Set to 0 to disable.
+ * @param fallback
+ * Set to F_true to print the fallback error message for unknown errors.
+ * @param thread
+ * The thread data.
+ *
+ * @see fll_error_print()
+ * @see controller_entry_print_error_cache()
+ */
+#ifndef _di_controller_entry_print_error_
+ extern void controller_entry_print_error(const bool is_entry, const fl_print_t print, const controller_cache_action_t cache, const f_status_t status, const f_string_t function, const bool fallback, controller_thread_t *thread) F_attribute_visibility_internal_d;
+#endif // _di_controller_entry_print_error_
+
+/**
+ * Print additional error/warning information in addition to existing error that is found within the cache.
+ *
+ * This is explicitly intended to be used in addition to the error message.
+ *
+ * This neither locks the thread nor does it check to see if output is enabled or disabled.
+ *
+ * @param is_entry
+ * If TRUE, then this loads as an entry.
+ * If FALSE, then this loads as an exit.
+ * @param output
+ * Designates how printing is to be performed.
+ * @param cache
+ * The action cache.
+ *
+ * @see controller_entry_actions_read()
+ * @see controller_entry_read()
+ */
+#ifndef _di_controller_entry_print_error_cache_
+ extern void controller_entry_print_error_cache(const bool is_entry, const fl_print_t output, const controller_cache_action_t cache) F_attribute_visibility_internal_d;
+#endif // _di_controller_entry_print_error_cache_
+
+/**
+ * Print the entry related file error, locking the print mutex during the print.
+ *
+ * @param is_entry
+ * If TRUE, then this loads as an entry.
+ * If FALSE, then this loads as an exit.
+ * @param print
+ * Designates how printing is to be performed.
+ * @param cache
+ * The action cache.
+ * @param status
+ * The status code to process.
+ * Make sure this has F_status_set_fine() called if the status code has any error or warning bits.
+ * @param function
+ * The name of the function where the error happened.
+ * Set to 0 to disable.
+ * @param fallback
+ * Set to F_true to print the fallback error message for unknown errors.
+ * @param name
+ * The name of the file or directory.
+ * @param operation
+ * The operation that fails, such as 'create' or 'access'.
+ * @param type
+ * A valid file type code from the fll_error_file_type enum.
+ * @param thread
+ * The thread data.
+ *
+ * @see fll_error_file_print()
+ * @see controller_entry_print_error_cache()
+ */
+#ifndef _di_controller_entry_print_error_file_
+ extern void controller_entry_print_error_file(const bool is_entry, const fl_print_t print, const controller_cache_action_t cache, const f_status_t status, const f_string_t function, const bool fallback, const f_string_t name, const f_string_t operation, const uint8_t type, controller_thread_t *thread) F_attribute_visibility_internal_d;
+#endif // _di_controller_entry_print_error_file_
+
+/**
+ * Print a message about an entry setting problem, with additional messages about the value.
+ *
+ * This is intended to be explicitly called by controller_entry_settings_read().
+ * This is intended only to be used for simple messages.
+ *
+ * @param is_entry
+ * If TRUE, then this loads as an entry.
+ * If FALSE, then this loads as an exit.
+ * @param print
+ * The error or warning output structure.
+ * @param before
+ * The string to add to the message being printed (before the value).
+ * @param range
+ * The range within the cache item buffer representing the value.
+ * @param after
+ * The string to add to the message being printed (after the value).
+ * @param thread
+ * The thread data.
+ * @param cache
+ * A structure for containing and caching relevant data.
+ *
+ * @see controller_entry_settings_read()
+ */
+#ifndef _di_controller_entry_setting_read_print_error_with_range_
+ extern void controller_entry_setting_read_print_error_with_range(const bool is_entry, const fl_print_t print, const f_string_t before, const f_string_range_t range, const f_string_t after, controller_thread_t * const thread, controller_cache_t * const cache) F_attribute_visibility_internal_d;
+#endif // _di_controller_entry_setting_read_print_error_with_range_
+
+/**
+ * Print a message for when an entry setting action has the incorrect number of parameters.
+ *
+ * @param global
+ * The global data.
+ * @param is_entry
+ * If TRUE, then this loads as an entry.
+ * If FALSE, then this loads as an exit.
+ * @param cache
+ * A structure for containing and caching relevant data.
+ * @param total
+ * The expected number of arguments.
+ */
+#ifndef _di_controller_entry_settings_read_print_setting_requires_exactly_
+ extern void controller_entry_settings_read_print_setting_requires_exactly(const controller_global_t global, const bool is_entry, const controller_cache_t cache, const f_number_unsigned_t total) F_attribute_visibility_internal_d;
+#endif // _di_controller_entry_settings_read_print_setting_requires_exactly_
+
+/**
+ * Print a message for when an entry setting action is unknown.
+ *
+ * @param global
+ * The global data.
+ * @param is_entry
+ * If TRUE, then this loads as an entry.
+ * If FALSE, then this loads as an exit.
+ * @param cache
+ * A structure for containing and caching relevant data.
+ */
+#ifndef _di_controller_entry_settings_read_print_setting_unknown_action_
+ extern void controller_entry_settings_read_print_setting_unknown_action(const controller_global_t global, const bool is_entry, const controller_cache_t cache) F_attribute_visibility_internal_d;
+#endif // _di_controller_entry_settings_read_print_setting_unknown_action_
+
+/**
+ * Print a message for when an entry setting action has an unknown value.
+ *
+ * @param global
+ * The global data.
+ * @param is_entry
+ * If TRUE, then this loads as an entry.
+ * If FALSE, then this loads as an exit.
+ * @param cache
+ * A structure for containing and caching relevant data.
+ * @param total
+ * The expected number of arguments.
+ * @param index
+ * The location in the content actions array representing the action value.
+ */
+#ifndef _di_controller_entry_settings_read_print_setting_unknown_action_value_
+ extern void controller_entry_settings_read_print_setting_unknown_action_value(const controller_global_t global, const bool is_entry, const controller_cache_t cache, const f_array_length_t index) F_attribute_visibility_internal_d;
+#endif // _di_controller_entry_settings_read_print_setting_unknown_action_value_
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // _PRIVATE_entry_print_h
--- /dev/null
+#include "../controller/controller.h"
+#include "../common/private-common.h"
+#include "../controller/private-controller.h"
+#include "../lock/private-lock.h"
+#include "../thread/private-thread.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef _di_controller_lock_create_
+ f_status_t controller_lock_create(controller_lock_t *lock) {
+
+ f_status_t status = f_thread_mutex_create(0, &lock->print);
+ if (F_status_is_error(status)) return status;
+
+ status = f_thread_mutex_create(0, &lock->alert);
+ if (F_status_is_error(status)) return status;
+
+ status = f_thread_lock_create(0, &lock->process);
+ if (F_status_is_error(status)) return status;
+
+ status = f_thread_lock_create(0, &lock->rule);
+ if (F_status_is_error(status)) return status;
+
+ status = f_thread_condition_create(0, &lock->alert_condition);
+ if (F_status_is_error(status)) return status;
+
+ return F_none;
+ }
+#endif // _di_controller_lock_create_
+
+#ifndef _di_controller_lock_read_
+ f_status_t controller_lock_read(const bool is_normal, controller_thread_t * const thread, f_thread_lock_t *lock) {
+
+ struct timespec time;
+
+ f_status_t status = F_none;
+
+ for (;;) {
+
+ controller_time(controller_thread_lock_read_timeout_seconds_d, controller_thread_lock_read_timeout_nanoseconds_d, &time);
+
+ status = f_thread_lock_read_timed(&time, lock);
+
+ if (status == F_time) {
+ if (!controller_thread_is_enabled(is_normal, thread)) {
+ return F_status_set_error(F_interrupt);
+ }
+ }
+ else {
+ break;
+ }
+ } // for
+
+ return status;
+ }
+#endif // _di_controller_lock_read_
+
+#ifndef _di_controller_lock_read_process_
+ f_status_t controller_lock_read_process(controller_process_t * const process, controller_thread_t * const thread, f_thread_lock_t *lock) {
+
+ return controller_lock_read_process_type(process->type, thread, lock);
+ }
+#endif // _di_controller_lock_read_process_
+
+#ifndef _di_controller_lock_read_process_type_
+ f_status_t controller_lock_read_process_type(const uint8_t type, controller_thread_t * const thread, f_thread_lock_t *lock) {
+
+ return controller_lock_read(type != controller_process_type_exit_e, thread, lock);
+ }
+#endif // _di_controller_lock_read_process_type_
+
+#ifndef _di_controller_lock_write_
+ f_status_t controller_lock_write(const bool is_normal, controller_thread_t * const thread, f_thread_lock_t *lock) {
+
+ struct timespec time;
+
+ f_status_t status = F_none;
+
+ for (;;) {
+
+ controller_time(controller_thread_lock_write_timeout_seconds_d, controller_thread_lock_write_timeout_nanoseconds_d, &time);
+
+ status = f_thread_lock_write_timed(&time, lock);
+
+ if (status == F_time) {
+ if (!controller_thread_is_enabled(is_normal, thread)) {
+ return F_status_set_error(F_interrupt);
+ }
+ }
+ else {
+ break;
+ }
+ } // for
+
+ return status;
+ }
+#endif // _di_controller_lock_write_
+
+#ifndef _di_controller_lock_write_process_
+ f_status_t controller_lock_write_process(controller_process_t * const process, controller_thread_t * const thread, f_thread_lock_t *lock) {
+
+ return controller_lock_write_process_type(process->type, thread, lock);
+ }
+#endif // _di_controller_lock_write_process_
+
+#ifndef _di_controller_lock_write_process_type_
+ f_status_t controller_lock_write_process_type(const uint8_t type, controller_thread_t * const thread, f_thread_lock_t *lock) {
+
+ return controller_lock_write(type != controller_process_type_exit_e, thread, lock);
+ }
+#endif // _di_controller_lock_write_process_type_
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
--- /dev/null
+/**
+ * FLL - Level 3
+ *
+ * Project: Controller
+ * API Version: 0.5
+ * Licenses: lgpl-2.1-or-later
+ */
+#ifndef _PRIVATE_lock_h
+#define _PRIVATE_lock_h
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Perform the initial, required, allocation for the lock.
+ *
+ * @param lock
+ * The lock to allocate.
+ *
+ * @return
+ * F_none on success.
+ *
+ * Errors (with error bit) from: f_thread_lock_delete().
+ * Errors (with error bit) from: f_thread_mutex_delete().
+ *
+ * @see f_thread_lock_delete()
+ * @see f_thread_mutex_delete()
+ */
+#ifndef _di_controller_lock_create_
+ extern f_status_t controller_lock_create(controller_lock_t *lock) F_attribute_visibility_internal_d;
+#endif // _di_controller_lock_create_
+
+/**
+ * Wait to get a read lock.
+ *
+ * Given a r/w lock, periodically check to see if main thread is disabled while waiting.
+ *
+ * @param is_normal
+ * If TRUE, then process as if this operates during a normal operation (entry and control).
+ * If FALSE, then process as if this operates during a an exit operation.
+ * @param thread
+ * The thread data used to determine if the main thread is disabled or not.
+ * @param lock
+ * The r/w lock to obtain a read lock on.
+ *
+ * @return
+ * F_none on success.
+ * F_status if main thread is disabled and write lock was never achieved.
+ *
+ * F_interrupt (with error bit set) on (exit) signal received, lock will not be set when this is returned.
+ *
+ * Status from: f_thread_lock_read_timed().
+ *
+ * Errors (with error bit) from: f_thread_lock_read_timed().
+ *
+ * @see f_thread_lock_read_timed()
+ */
+#ifndef _di_controller_lock_read_
+ extern f_status_t controller_lock_read(const bool is_normal, controller_thread_t * const thread, f_thread_lock_t *lock) F_attribute_visibility_internal_d;
+#endif // _di_controller_lock_read_
+
+/**
+ * Wait to get a read lock for some process.
+ *
+ * Given a r/w lock, periodically check to see if main thread is disabled while waiting.
+ *
+ * @param process
+ * The process to use when checking if thread is enabled.
+ * @param thread
+ * The thread data used to determine if the main thread is disabled or not.
+ * @param lock
+ * The r/w lock to obtain a read lock on.
+ *
+ * @return
+ *
+ * Status from: controller_lock_read().
+ *
+ * Errors (with error bit) from: controller_lock_read().
+ *
+ * @see controller_lock_read()
+ */
+#ifndef _di_controller_lock_read_process_
+ extern f_status_t controller_lock_read_process(controller_process_t * const process, controller_thread_t * const thread, f_thread_lock_t *lock) F_attribute_visibility_internal_d;
+#endif // _di_controller_lock_read_process_
+
+/**
+ * Wait to get a read lock for some process type.
+ *
+ * Given a r/w lock, periodically check to see if main thread is disabled while waiting.
+ *
+ * @param type
+ * The process type to use when checking if thread is enabled.
+ * @param thread
+ * The thread data used to determine if the main thread is disabled or not.
+ * @param lock
+ * The r/w lock to obtain a read lock on.
+ *
+ * @return
+ *
+ * Status from: controller_lock_read().
+ *
+ * Errors (with error bit) from: controller_lock_read().
+ *
+ * @see controller_lock_read()
+ */
+#ifndef _di_controller_lock_read_process_type_
+ extern f_status_t controller_lock_read_process_type(const uint8_t type, controller_thread_t * const thread, f_thread_lock_t *lock) F_attribute_visibility_internal_d;
+#endif // _di_controller_lock_read_process_type_
+
+/**
+ * Wait to get a write lock.
+ *
+ * Given a r/w lock, periodically check to see if main thread is disabled while waiting.
+ *
+ * @param is_normal
+ * If TRUE, then process as if this operates during a normal operation (entry and control).
+ * If FALSE, then process as if this operates during a an exit operation.
+ * @param thread
+ * The thread data used to determine if the main thread is disabled or not.
+ * @param lock
+ * The r/w lock to obtain a write lock on.
+ *
+ * @return
+ * F_none on success.
+ * F_status if main thread is disabled and write lock was never achieved.
+ *
+ * F_interrupt (with error bit set) on (exit) signal received, lock will not be set when this is returned.
+ *
+ * Status from: f_thread_lock_write_timed().
+ *
+ * Errors (with error bit) from: f_thread_lock_write_timed().
+ *
+ * @see f_thread_lock_write_timed()
+ */
+#ifndef _di_controller_lock_write_
+ extern f_status_t controller_lock_write(const bool is_normal, controller_thread_t * const thread, f_thread_lock_t *lock) F_attribute_visibility_internal_d;
+#endif // _di_controller_lock_write_
+
+/**
+ * Wait to get a write lock for some process.
+ *
+ * Given a r/w lock, periodically check to see if main thread is disabled while waiting.
+ *
+ * @param process
+ * The process to use when checking if thread is enabled.
+ * @param thread
+ * The thread data used to determine if the main thread is disabled or not.
+ * @param lock
+ * The r/w lock to obtain a write lock on.
+ *
+ * @return
+ *
+ * Status from: controller_lock_write_process_type().
+ *
+ * Errors (with error bit) from: controller_lock_write_process_type().
+ *
+ * @see controller_lock_write_process_type()
+ */
+#ifndef _di_controller_lock_write_process_
+ extern f_status_t controller_lock_write_process(controller_process_t * const process, controller_thread_t * const thread, f_thread_lock_t *lock) F_attribute_visibility_internal_d;
+#endif // _di_controller_lock_write_process_
+
+/**
+ * Wait to get a write lock for some process type.
+ *
+ * Given a r/w lock, periodically check to see if main thread is disabled while waiting.
+ *
+ * @param type
+ * The process type to use when checking if thread is enabled.
+ * @param thread
+ * The thread data used to determine if the main thread is disabled or not.
+ * @param lock
+ * The r/w lock to obtain a write lock on.
+ *
+ * @return
+ *
+ * Status from: controller_lock_write().
+ *
+ * Errors (with error bit) from: controller_lock_write().
+ *
+ * @see controller_lock_write()
+ */
+#ifndef _di_controller_lock_write_process_type_
+ extern f_status_t controller_lock_write_process_type(const uint8_t type, controller_thread_t * const thread, f_thread_lock_t *lock) F_attribute_visibility_internal_d;
+#endif // _di_controller_lock_write_process_type_
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // _PRIVATE_lock_h
--- /dev/null
+#include "../controller/controller.h"
+#include "../common/private-common.h"
+#include "../lock/private-lock_print.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef _di_controller_lock_print_error_critical_
+ void controller_lock_print_error_critical(const fl_print_t print, const f_status_t status, const bool read, controller_thread_t *thread) {
+
+ // A signal is not an error.
+ if (status == F_interrupt) return;
+
+ if (print.verbosity != f_console_verbosity_quiet_e) {
+ controller_lock_print(print.to, thread);
+
+ fl_print_format("%c%[%SThe pid file '%]", print.to.stream, f_string_eol_s[0], print.context, print.prefix ? print.prefix : f_string_empty_s, print.context);
+ fl_print_format("%['Critical failure while attempting to establish '%]", print.to.stream, print.context, print.context);
+ fl_print_format("%[%s lock%]", print.to.stream, print.notable, read ? "read" : "write", print.notable);
+
+ if (status != F_failure) {
+ fl_print_format(" %['due to%] ", print.to.stream, print.context, print.context);
+
+ if (status == F_parameter) {
+ fl_print_format("%[Invalid Parameter%]", print.to.stream, print.notable, print.notable);
+ }
+ else if (status == F_deadlock) {
+ fl_print_format("%[Deadlock%]", print.to.stream, print.notable, print.notable);
+ }
+ else if (status == F_resource_not) {
+ fl_print_format("%[Too Many Locks%]", print.to.stream, print.notable, print.notable);
+ }
+ else {
+ fl_print_format("%[Unknown Error%]", print.to.stream, print.notable, print.notable);
+ }
+ }
+
+ fl_print_format("%['.%]%c", print.to.stream, print.context, print.context, f_string_eol_s[0]);
+
+ controller_unlock_print_flush(print.to, thread);
+ }
+ }
+#endif // _di_controller_lock_print_error_critical_
+
+#ifndef _di_controller_lock_print_
+ void controller_lock_print(const f_file_t to, controller_thread_t * const thread) {
+
+ if (thread) {
+ f_thread_mutex_lock(&thread->lock.print);
+ }
+
+ flockfile(to.stream);
+ }
+#endif // _di_controller_lock_print_
+
+#ifndef _di_controller_unlock_print_flush_
+ void controller_unlock_print_flush(const f_file_t to, controller_thread_t * const thread) {
+
+ fflush(to.stream);
+ funlockfile(to.stream);
+
+ if (thread) {
+ f_thread_mutex_unlock(&thread->lock.print);
+ }
+ }
+#endif // _di_controller_unlock_print_flush_
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
--- /dev/null
+/**
+ * FLL - Level 3
+ *
+ * Project: Controller
+ * API Version: 0.5
+ * Licenses: lgpl-2.1-or-later
+ */
+#ifndef _PRIVATE_lock_print_h
+#define _PRIVATE_lock_print_h
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Print a r/w lock related error message, locking the print mutex during the print.
+ *
+ * This will ignore F_interrupt and not print any messages, if passed.
+ *
+ * @param print
+ * Designates how printing is to be performed.
+ * @param status
+ * The status code to process.
+ * Make sure this has F_status_set_fine() called if the status code has any error or warning bits.
+ * @param read
+ * If TRUE, then this is for a read lock.
+ * If FALSE, then this is for a write lock.
+ * @param thread
+ * The thread data.
+ */
+#ifndef _di_controller_lock_print_error_critical_
+ extern void controller_lock_print_error_critical(const fl_print_t print, const f_status_t status, const bool read, controller_thread_t *thread) F_attribute_visibility_internal_d;
+#endif // _di_controller_lock_print_error_critical_
+
+/**
+ * Lock the mutex and the stream.
+ *
+ * This is implemented as a compliment to controller_unlock_print_flush() for consistency reasons.
+ *
+ * @param to
+ * The file stream to lock.
+ * @param thread
+ * The thread containing the print mutex to lock.
+ *
+ * @see flockfile()
+ *
+ * @see f_thread_mutex_unlock()
+ */
+#ifndef _di_controller_lock_print_
+ extern void controller_lock_print(const f_file_t to, controller_thread_t * const thread) F_attribute_visibility_internal_d;
+#endif // _di_controller_lock_print_
+
+/**
+ * Flush the stream buffer and then unlock the mutex.
+ *
+ * This unlocks both the stream and the mutex locks.
+ *
+ * Weird behavior was observed when piping data from this program.
+ * The behavior appears related to how this handles locks in addition to the file streams own locking mechanisms.
+ *
+ * As a work-around, this performs a flush immediately before unlocking the print mutex.
+ *
+ * @param to
+ * The file stream to unlock and flush.
+ * @param thread
+ * The thread containing the print mutex to unlock.
+ *
+ * @see funlockfile()
+ *
+ * @see f_thread_mutex_unlock()
+ */
+#ifndef _di_controller_unlock_print_flush_
+ void controller_unlock_print_flush(const f_file_t to, controller_thread_t * const thread) F_attribute_visibility_internal_d;
+#endif // _di_controller_unlock_print_flush_
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // _PRIVATE_lock_print_h
-#include "controller.h"
+#include "controller/controller.h"
int main(const int argc, const f_string_t *argv) {
--- /dev/null
+#include "../controller/controller.h"
+#include "../common/private-common.h"
+#include "../controller/private-controller.h"
+#include "../lock/private-lock.h"
+#include "../lock/private-lock_print.h"
+#include "../process/private-process.h"
+#include "../rule/private-rule.h"
+#include "../thread/private-thread.h"
+#include "../thread/private-thread_process.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef _di_controller_process_find_
+ f_status_t controller_process_find(const f_array_length_t action, const f_string_static_t alias, const controller_processs_t processs, f_array_length_t *at) {
+
+ if (!alias.used) return F_none;
+ if (!processs.used) return F_false;
+
+ for (f_array_length_t i = 0; i < processs.used; ++i) {
+
+ if (processs.array[i] && processs.array[i]->action == action && fl_string_dynamic_compare(alias, processs.array[i]->rule.alias) == F_equal_to) {
+ if (at) *at = i;
+
+ return F_true;
+ }
+ } // for
+
+ return F_false;
+ }
+#endif // _di_controller_process_find_
+
+#ifndef _di_controller_process_prepare_
+ f_status_t controller_process_prepare(const controller_global_t global, const bool is_normal, const uint8_t action, const f_string_static_t alias, f_array_length_t *id) {
+
+ f_status_t status = F_none;
+
+ if (controller_process_find(action, alias, global.thread->processs, id) == F_false) {
+ f_thread_unlock(&global.thread->lock.process);
+
+ status = controller_lock_write(is_normal, global.thread, &global.thread->lock.process);
+
+ if (F_status_is_error(status)) {
+ controller_lock_print_error_critical(global.main->error, F_status_set_fine(status), F_false, global.thread);
+ }
+ else {
+ status = controller_processs_increase(&global.thread->processs);
+ }
+
+ if (F_status_is_error_not(status) && global.thread->processs.array[global.thread->processs.used]) {
+
+ controller_process_t *process = global.thread->processs.array[global.thread->processs.used];
+
+ status = controller_lock_write(is_normal, global.thread, &process->lock);
+
+ if (F_status_is_error(status)) {
+ controller_lock_print_error_critical(global.main->error, F_status_set_fine(status), F_false, global.thread);
+ }
+ else {
+ process->action = action;
+ process->rule.alias.used = 0;
+
+ status = f_string_dynamic_append(alias, &process->rule.alias);
+
+ if (F_status_is_error_not(status)) {
+ status = f_string_dynamic_terminate_after(&process->rule.alias);
+
+ if (F_status_is_error_not(status)) {
+ process->id = global.thread->processs.used++;
+ status = F_none;
+
+ if (id) {
+ *id = process->id;
+ }
+ }
+ }
+
+ f_thread_unlock(&process->lock);
+ }
+ }
+
+ f_thread_unlock(&global.thread->lock.process);
+
+ // The read lock must be restored on return.
+ const f_status_t status_lock = controller_lock_read(is_normal, global.thread, &global.thread->lock.process);
+
+ if (F_status_is_error(status_lock)) {
+ return F_status_set_error(F_lock);
+ }
+ }
+ else {
+ status = F_found;
+ }
+
+ return status;
+ }
+#endif // _di_controller_process_prepare_
+
+#ifndef _di_controller_process_prepare_process_type_
+ f_status_t controller_process_prepare_process_type(const controller_global_t global, const uint8_t type, const uint8_t action, const f_string_static_t alias, f_array_length_t *id) {
+
+ return controller_process_prepare(global, type != controller_process_type_exit_e, action, alias, id);
+ }
+#endif // _di_controller_process_prepare_process_type_
+
+#ifndef _di_controller_process_wait_
+ f_status_t controller_process_wait(const controller_global_t global, controller_process_t * const process) {
+
+ if (!controller_thread_is_enabled_process(process, global.thread)) {
+ return F_status_set_error(F_interrupt);
+ }
+
+ struct timespec time;
+
+ f_status_t status = F_none;
+ f_status_t status_lock = F_none;
+
+ uint8_t count = 0;
+
+ do {
+ f_thread_mutex_lock(&process->wait_lock);
+
+ if (count < controller_thread_wait_timeout_1_before_d) {
+ controller_time(controller_thread_wait_timeout_1_seconds_d, controller_thread_wait_timeout_1_nanoseconds_d, &time);
+ }
+ else if (count < controller_thread_wait_timeout_2_before_d) {
+ controller_time(controller_thread_wait_timeout_2_seconds_d, controller_thread_wait_timeout_2_nanoseconds_d, &time);
+ }
+ else if (count < controller_thread_wait_timeout_3_before_d) {
+ controller_time(controller_thread_wait_timeout_3_seconds_d, controller_thread_wait_timeout_3_nanoseconds_d, &time);
+ }
+ else {
+ controller_time(controller_thread_wait_timeout_4_seconds_d, controller_thread_wait_timeout_4_nanoseconds_d, &time);
+ }
+
+ status = f_thread_condition_wait_timed(&time, &process->wait, &process->wait_lock);
+
+ f_thread_mutex_unlock(&process->wait_lock);
+
+ if (!controller_thread_is_enabled_process(process, global.thread)) {
+ return F_status_set_error(F_interrupt);
+ }
+
+ if (F_status_is_error(status)) break;
+
+ status_lock = controller_lock_read_process(process, global.thread, &process->lock);
+
+ if (F_status_is_error(status_lock)) {
+ controller_lock_print_error_critical(global.main->error, F_status_set_fine(status_lock), F_true, global.thread);
+
+ break;
+ }
+
+ if (!controller_rule_status_is_available(process->action, process->rule) && !(process->state == controller_process_state_active_e || process->state == controller_process_state_busy_e)) {
+ f_thread_unlock(&process->lock);
+
+ return F_none;
+ }
+
+ if (status != F_time) {
+
+ // move up the wait timer after a trigger was received.
+ if (count < controller_thread_wait_timeout_2_before_d) {
+ count = 0;
+ }
+ else if (count < controller_thread_wait_timeout_3_before_d) {
+ count = controller_thread_wait_timeout_1_before_d;
+ }
+ else {
+ count = controller_thread_wait_timeout_2_before_d;
+ }
+ }
+
+ f_thread_unlock(&process->lock);
+
+ if (count < controller_thread_wait_timeout_3_before_d) {
+ ++count;
+ }
+
+ } while (status == F_time && controller_thread_is_enabled_process(process, global.thread));
+
+ return status;
+ }
+#endif // _di_controller_process_wait_
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
--- /dev/null
+/**
+ * FLL - Level 3
+ *
+ * Project: Controller
+ * API Version: 0.5
+ * Licenses: lgpl-2.1-or-later
+ */
+#ifndef _PRIVATE_process_h
+#define _PRIVATE_process_h
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Prepare the process.
+ *
+ * The process is initialized with the process id, the rule alias, and the rule action type.
+ * These are the necessary parts for uniquely identifying the process.
+ *
+ * If a process by the given Rule alias and Rule Action already exists, then nothing is done.
+ *
+ * This requires that a global.thread->lock.process lock be set on process->lock before being called.
+ *
+ * @param global
+ * The global data.
+ * @param is_normal
+ * If TRUE, then process as if this operates during a normal operation (entry and control).
+ * If FALSE, then process as if this operates during a an exit operation.
+ * @param action
+ * The Rule Action to use.
+ * @param alias
+ * The Rule alias to use.
+ * @param id
+ * (optional) The process ID when found or created.
+ * Set to NULL to not use.
+ *
+ * @return
+ * F_none on success.
+ * F_found on success, but nothing was done because an existing process was found.
+ *
+ * F_lock (with error bit) if failed to re-establish read lock on global.thread->lock.process while returning.
+ *
+ * Errors (with error bit) from: f_string_dynamic_append().
+ * Errors (with error bit) from: f_string_dynamic_terminate_after().
+ *
+ * Errors (with error bit) from: controller_lock_read().
+ * Errors (with error bit) from: controller_lock_write().
+ *
+ * @see f_string_dynamic_append()
+ * @see f_string_dynamic_terminate_after()
+ * @see controller_lock_read()
+ * @see controller_lock_write()
+ */
+#ifndef _di_controller_process_prepare_
+ extern f_status_t controller_process_prepare(const controller_global_t global, const bool is_normal, const uint8_t action, const f_string_static_t alias, f_array_length_t *id) F_attribute_visibility_internal_d;
+#endif // _di_controller_process_prepare_
+
+/**
+ * Prepare the process for some process type.
+ *
+ * The process is initialized with the process id, the rule alias, and the rule action type.
+ * These are the necessary parts for uniquely identifying the process.
+ *
+ * If a process by the given Rule alias and Rule Action already exists, then nothing is done.
+ *
+ * This requires that a global.thread->lock.process lock be set on process->lock before being called.
+ *
+ * @param global
+ * The global data.
+ * @param type
+ * The process type to use when checking if thread is enabled.
+ * @param action
+ * The Rule Action to use.
+ * @param alias
+ * The Rule alias to use.
+ * @param id
+ * (optional) The process ID when found or created.
+ * Set to NULL to not use.
+ *
+ * @return
+ * Success from: controller_process_prepare()
+ *
+ * Errors (with error bit) from: controller_process_prepare().
+ *
+ * @see controller_process_prepare()
+ */
+#ifndef _di_controller_process_prepare_process_type_
+ extern f_status_t controller_process_prepare_process_type(const controller_global_t global, const uint8_t type, const uint8_t action, const f_string_static_t alias, f_array_length_t *id) F_attribute_visibility_internal_d;
+#endif // _di_controller_process_prepare_process_type_
+
+/**
+ * Find an existing process, for the given Rule Action.
+ *
+ * Do not confuse this with a process in the context of a PID.
+ * This is a stucture for the current processing of some rule.
+ *
+ * This does not do any locking or unlocking for the processs data, be sure to lock appropriately before and after calling this.
+ *
+ * @param action
+ * The Rule Action to find.
+ * @param alias
+ * The Rule alias to find.
+ * @param processs
+ * The array of processes to.
+ * @param at
+ * The location within processs the id was found.
+ * (optional) Set to NULL to disable.
+ *
+ * @return
+ * F_none if not given a valid id to search.
+ * F_false if there is no process found.
+ * F_true if there is a process found (address is stored in "at").
+ */
+#ifndef _di_controller_process_find_
+ f_status_t controller_process_find(const f_array_length_t action, const f_string_static_t alias, const controller_processs_t processs, f_array_length_t *at) F_attribute_visibility_internal_d;
+#endif // _di_controller_process_find_
+
+/***
+ * Safely wait for a process, periodically checking to see if process completed or check if exiting.
+ *
+ * @param global
+ * The global data.
+ * @param process
+ * The process to wait on.
+ *
+ * @return
+ * F_none on success.
+ *
+ * F_interrupt (with error bit) on receiving a process signal, such as an interrupt signal.
+ *
+ * Success from: f_thread_condition_wait_timed().
+ *
+ * Errors (with error bit) from: f_thread_condition_wait_timed().
+ *
+ * @see f_thread_condition_wait_timed()
+ */
+#ifndef _di_controller_process_wait_
+ extern f_status_t controller_process_wait(const controller_global_t global, controller_process_t * const process) F_attribute_visibility_internal_d;
+#endif // _di_controller_process_wait_
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // _PRIVATE_process_h
--- /dev/null
+#include "../controller/controller.h"
+#include "../common/private-common.h"
+#include "../controller/private-controller.h"
+#include "../controller/private-controller_print.h"
+#include "../lock/private-lock.h"
+#include "../lock/private-lock_print.h"
+#include "../process/private-process.h"
+#include "../rule/private-rule.h"
+#include "../rule/private-rule_print.h"
+#include "../thread/private-thread.h"
+#include "../thread/private-thread_process.h"
+#include "../thread/private-thread_signal.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef _di_controller_rule_action_method_name_
+ f_string_static_t controller_rule_action_method_name(const uint8_t type) {
+
+ f_string_static_t buffer = f_string_static_t_initialize;
+
+ switch (type) {
+ case controller_rule_action_method_extended_e:
+ buffer.string = controller_rule_action_method_string_extended_s;
+ buffer.used = controller_rule_action_method_string_extended_s_length;
+ break;
+
+ case controller_rule_action_method_extended_list_e:
+ buffer.string = controller_rule_action_method_string_extended_list_s;
+ buffer.used = controller_rule_action_method_string_extended_list_s_length;
+ break;
+ }
+
+ buffer.size = buffer.used;
+
+ return buffer;
+ }
+#endif // _di_controller_rule_action_method_name_
+
+#ifndef _di_controller_rule_find_
+ f_status_t controller_rule_find(const f_string_static_t alias, const controller_rules_t rules, f_array_length_t *at) {
+
+ if (!alias.used) return F_none;
+ if (!rules.used) return F_false;
+
+ for (f_array_length_t i = 0; i < rules.used; ++i) {
+
+ if (fl_string_dynamic_compare(alias, rules.array[i].alias) == F_equal_to) {
+ if (at) *at = i;
+
+ return F_true;
+ }
+ } // for
+
+ return F_false;
+ }
+#endif // _di_controller_rule_find_
+
+#ifndef _di_controller_rule_parameters_read_
+ f_status_t controller_rule_parameters_read(const controller_global_t global, const f_string_static_t buffer, f_fss_object_t *object, f_fss_content_t *content, f_string_dynamics_t *parameters) {
+
+ f_status_t status = F_none;
+
+ parameters->used = 0;
+
+ if (object && object->start <= object->start) {
+
+ status = f_string_dynamics_increase(controller_common_allocation_small_d, parameters);
+
+ if (F_status_is_error(status)) {
+ controller_print_error(global.thread, global.main->error, F_status_set_fine(status), "f_string_dynamics_increase", F_true);
+
+ return status;
+ }
+
+ parameters->array[parameters->used].used = 0;
+
+ status = f_string_dynamic_partial_append_nulless(buffer, *object, ¶meters->array[0]);
+
+ if (F_status_is_error(status)) {
+ controller_print_error(global.thread, global.main->error, F_status_set_fine(status), "f_string_dynamic_partial_append_nulless", F_true);
+
+ return status;
+ }
+
+ status = f_string_dynamic_terminate_after(¶meters->array[0]);
+
+ if (F_status_is_error(status)) {
+ controller_print_error(global.thread, global.main->error, F_status_set_fine(status), "f_string_dynamic_terminate_after", F_true);
+
+ return status;
+ }
+
+ ++parameters->used;
+ }
+
+ if (content && content->used) {
+
+ for (f_array_length_t i = 0; i < content->used; ++i) {
+
+ if (content->array[i].start > content->array[i].start) continue;
+
+ status = f_string_dynamics_increase(controller_common_allocation_small_d, parameters);
+
+ if (F_status_is_error(status)) {
+ controller_print_error(global.thread, global.main->error, F_status_set_fine(status), "f_string_dynamics_increase", F_true);
+
+ return status;
+ }
+
+ parameters->array[parameters->used].used = 0;
+
+ status = f_string_dynamic_partial_append_nulless(buffer, content->array[i], ¶meters->array[parameters->used]);
+
+ if (F_status_is_error(status)) {
+ controller_print_error(global.thread, global.main->error, F_status_set_fine(status), "f_string_dynamic_partial_append_nulless", F_true);
+
+ return status;
+ }
+
+ status = f_string_dynamic_terminate_after(¶meters->array[parameters->used]);
+
+ if (F_status_is_error(status)) {
+ controller_print_error(global.thread, global.main->error, F_status_set_fine(status), "f_string_dynamic_terminate_after", F_true);
+
+ return status;
+ }
+
+ ++parameters->used;
+ } // for
+ }
+
+ return F_none;
+ }
+#endif // _di_controller_rule_parameters_read_
+
+#ifndef _di_controller_rule_action_type_to_action_execute_type_
+ uint8_t controller_rule_action_type_to_action_execute_type(const uint8_t type) {
+
+ if (type == controller_rule_action_type_freeze_e) {
+ return controller_rule_action_type_execute_freeze_e;
+ }
+
+ if (type == controller_rule_action_type_kill_e) {
+ return controller_rule_action_type_execute_kill_e;
+ }
+
+ if (type == controller_rule_action_type_pause_e) {
+ return controller_rule_action_type_execute_pause_e;
+ }
+
+ if (type == controller_rule_action_type_reload_e) {
+ return controller_rule_action_type_execute_reload_e;
+ }
+
+ if (type == controller_rule_action_type_restart_e) {
+ return controller_rule_action_type_execute_restart_e;
+ }
+
+ if (type == controller_rule_action_type_resume_e) {
+ return controller_rule_action_type_execute_resume_e;
+ }
+
+ if (type == controller_rule_action_type_start_e) {
+ return controller_rule_action_type_execute_start_e;
+ }
+
+ if (type == controller_rule_action_type_stop_e) {
+ return controller_rule_action_type_execute_stop_e;
+ }
+
+ if (type == controller_rule_action_type_thaw_e) {
+ return controller_rule_action_type_execute_thaw_e;
+ }
+
+ return controller_rule_action_type_execute__enum_size_e;
+ }
+#endif // _di_controller_rule_action_type_to_action_execute_type_
+
+#ifndef _di_controller_rule_action_type_name_
+ f_string_static_t controller_rule_action_type_name(const uint8_t type) {
+
+ f_string_static_t buffer = f_string_static_t_initialize;
+
+ switch (type) {
+ case controller_rule_action_type_freeze_e:
+ buffer.string = controller_freeze_s;
+ buffer.used = controller_freeze_s_length;
+ break;
+
+ case controller_rule_action_type_group_e:
+ buffer.string = controller_group_s;
+ buffer.used = controller_group_s_length;
+ break;
+
+ case controller_rule_action_type_kill_e:
+ buffer.string = controller_kill_s;
+ buffer.used = controller_kill_s_length;
+ break;
+
+ case controller_rule_action_type_pause_e:
+ buffer.string = controller_pause_s;
+ buffer.used = controller_pause_s_length;
+ break;
+
+ case controller_rule_action_type_pid_file_e:
+ buffer.string = controller_pid_file_s;
+ buffer.used = controller_pid_file_s_length;
+ break;
+
+ case controller_rule_action_type_rerun_e:
+ buffer.string = controller_rerun_s;
+ buffer.used = controller_rerun_s_length;
+ break;
+
+ case controller_rule_action_type_reload_e:
+ buffer.string = controller_reload_s;
+ buffer.used = controller_reload_s_length;
+ break;
+
+ case controller_rule_action_type_restart_e:
+ buffer.string = controller_restart_s;
+ buffer.used = controller_restart_s_length;
+ break;
+
+ case controller_rule_action_type_resume_e:
+ buffer.string = controller_resume_s;
+ buffer.used = controller_resume_s_length;
+ break;
+
+ case controller_rule_action_type_start_e:
+ buffer.string = controller_start_s;
+ buffer.used = controller_start_s_length;
+ break;
+
+ case controller_rule_action_type_stop_e:
+ buffer.string = controller_stop_s;
+ buffer.used = controller_stop_s_length;
+ break;
+
+ case controller_rule_action_type_thaw_e:
+ buffer.string = controller_thaw_s;
+ buffer.used = controller_thaw_s_length;
+ break;
+
+ case controller_rule_action_type_user_e:
+ buffer.string = controller_user_s;
+ buffer.used = controller_user_s_length;
+ break;
+
+ case controller_rule_action_type_with_e:
+ buffer.string = controller_with_s;
+ buffer.used = controller_with_s_length;
+ break;
+ }
+
+ buffer.size = buffer.used;
+
+ return buffer;
+ }
+#endif // _di_controller_rule_action_type_name_
+
+#ifndef _di_controller_rule_action_type_execute_name_
+ f_string_static_t controller_rule_action_type_execute_name(const uint8_t type) {
+
+ f_string_static_t buffer = f_string_static_t_initialize;
+
+ switch (type) {
+ case controller_rule_action_type_execute_freeze_e:
+ buffer.string = controller_freeze_s;
+ buffer.used = controller_freeze_s_length;
+ break;
+
+ case controller_rule_action_type_execute_kill_e:
+ buffer.string = controller_kill_s;
+ buffer.used = controller_kill_s_length;
+ break;
+
+ case controller_rule_action_type_execute_pause_e:
+ buffer.string = controller_pause_s;
+ buffer.used = controller_pause_s_length;
+ break;
+
+ case controller_rule_action_type_execute_reload_e:
+ buffer.string = controller_reload_s;
+ buffer.used = controller_reload_s_length;
+ break;
+
+ case controller_rule_action_type_execute_restart_e:
+ buffer.string = controller_restart_s;
+ buffer.used = controller_restart_s_length;
+ break;
+
+ case controller_rule_action_type_execute_resume_e:
+ buffer.string = controller_resume_s;
+ buffer.used = controller_resume_s_length;
+ break;
+
+ case controller_rule_action_type_execute_start_e:
+ buffer.string = controller_start_s;
+ buffer.used = controller_start_s_length;
+ break;
+
+ case controller_rule_action_type_execute_stop_e:
+ buffer.string = controller_stop_s;
+ buffer.used = controller_stop_s_length;
+ break;
+
+ case controller_rule_action_type_execute_thaw_e:
+ buffer.string = controller_thaw_s;
+ buffer.used = controller_thaw_s_length;
+ break;
+ }
+
+ buffer.size = buffer.used;
+
+ return buffer;
+ }
+#endif // _di_controller_rule_action_type_execute_name_
+
+#ifndef _di_controller_rule_action_read_
+ f_status_t controller_rule_action_read(const controller_global_t global, const bool is_normal, const uint8_t type, const uint8_t method, controller_cache_t * const cache, controller_rule_item_t *item, controller_rule_actions_t *actions, f_string_range_t *range) {
+
+ f_status_t status = F_none;
+
+ if (method == controller_rule_action_method_extended_list_e) {
+ cache->comments.used = 0;
+ cache->delimits.used = 0;
+ cache->content_action.used = 0;
+ cache->content_actions.used = 0;
+ cache->object_actions.used = 0;
+
+ if (actions->size) {
+ actions->array[actions->used].parameters.used = 0;
+ }
+
+ {
+ controller_state_interrupt_t custom = macro_controller_state_interrupt_t_initialize(is_normal, global.thread);
+ f_state_t state = macro_f_state_t_initialize(controller_common_allocation_large_d, controller_common_allocation_small_d, 0, &controller_thread_signal_state_fss, 0, (void *) &custom, 0);
+
+ status = fl_fss_extended_list_content_read(cache->buffer_item, state, range, &cache->content_action, &cache->delimits, &cache->comments);
+ }
+
+ if (F_status_is_error(status)) {
+ controller_print_error(global.thread, global.main->error, F_status_set_fine(status), "fl_fss_extended_list_content_read", F_true);
+ }
+ else if (status == F_fss_found_content) {
+ status = fl_fss_apply_delimit(cache->delimits, &cache->buffer_item);
+
+ if (F_status_is_error(status)) {
+ controller_print_error(global.thread, global.main->error, F_status_set_fine(status), "fl_fss_apply_delimit", F_true);
+ }
+ else {
+
+ // "script" and "utility" types use the entire content and can be directly passed through.
+ if (item->type == controller_rule_item_type_script_e || item->type == controller_rule_item_type_utility_e) {
+ actions->array[actions->used].parameters.used = 0;
+
+ status = f_string_dynamics_increase(controller_common_allocation_small_d, &actions->array[actions->used].parameters);
+
+ if (F_status_is_error(status)) {
+ controller_print_error(global.thread, global.main->error, F_status_set_fine(status), "f_string_dynamics_increase", F_true);
+ }
+ else {
+ actions->array[actions->used].type = type;
+ actions->array[actions->used].line = cache->action.line_action;
+ actions->array[actions->used].parameters.used = 0;
+ actions->array[actions->used].status = F_known_not;
+
+ status = f_string_dynamic_partial_append_nulless(cache->buffer_item, cache->content_action.array[0], &actions->array[actions->used].parameters.array[0]);
+
+ if (F_status_is_error(status)) {
+ controller_print_error(global.thread, global.main->error, F_status_set_fine(status), "f_string_dynamic_append_nulless", F_true);
+ }
+
+ status = f_string_dynamic_terminate_after(&actions->array[actions->used].parameters.array[0]);
+
+ if (F_status_is_error(status)) {
+ controller_print_error(global.thread, global.main->error, F_status_set_fine(status), "f_string_dynamic_terminate_after", F_true);
+ }
+ else {
+ actions->array[actions->used++].parameters.used = 1;
+ }
+ }
+
+ return status;
+ }
+
+ {
+ controller_state_interrupt_t custom = macro_controller_state_interrupt_t_initialize(is_normal, global.thread);
+ f_state_t state = macro_f_state_t_initialize(controller_common_allocation_large_d, controller_common_allocation_small_d, 0, &controller_thread_signal_state_fss, 0, (void *) &custom, 0);
+
+ cache->delimits.used = 0;
+
+ // the object_actions and content_actions caches are being used for the purposes of getting the parameters a given the action.
+ status = fll_fss_extended_read(cache->buffer_item, state, &cache->content_action.array[0], &cache->object_actions, &cache->content_actions, 0, 0, &cache->delimits, 0);
+ }
+
+ if (F_status_is_error(status)) {
+ controller_print_error(global.thread, global.main->error, F_status_set_fine(status), "fll_fss_extended_read", F_true);
+ }
+ else {
+ status = fl_fss_apply_delimit(cache->delimits, &cache->buffer_item);
+
+ if (F_status_is_error(status)) {
+ controller_print_error(global.thread, global.main->error, F_status_set_fine(status), "fl_fss_apply_delimit", F_true);
+ }
+ else {
+ f_array_length_t i = 0;
+ f_array_length_t j = 0;
+
+ for (; i < cache->object_actions.used; ++i) {
+ status = controller_rule_actions_increase_by(controller_common_allocation_small_d, actions);
+
+ if (F_status_is_error(status)) {
+ controller_print_error(global.thread, global.main->error, F_status_set_fine(status), "controller_rule_actions_increase_by", F_true);
+
+ break;
+ }
+
+ 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)) {
+ controller_print_error(global.thread, global.main->error, F_status_set_fine(status), "f_fss_count_lines", F_true);
+
+ break;
+ }
+
+ actions->array[actions->used].type = type;
+ actions->array[actions->used].line += ++item->line;
+ actions->array[actions->used].parameters.used = 0;
+ actions->array[actions->used].status = F_known_not;
+
+ status = f_string_dynamics_increase(controller_common_allocation_small_d, &actions->array[actions->used].parameters);
+
+ if (F_status_is_error(status)) {
+ controller_print_error(global.thread, global.main->error, F_status_set_fine(status), "f_string_dynamics_increase", F_true);
+
+ actions->array[actions->used].status = controller_status_simplify_error(F_status_set_fine(status));
+
+ break;
+ }
+
+ status = controller_rule_parameters_read(global, cache->buffer_item, &cache->object_actions.array[i], &cache->content_actions.array[i], &actions->array[actions->used].parameters);
+
+ actions->array[actions->used++].status = controller_status_simplify_error(F_status_set_fine(status));
+ } // for
+
+ range->start = cache->content_action.array[0].start;
+ }
+ }
+ }
+ }
+ else {
+ status = F_data_not;
+ }
+ }
+ else {
+ cache->content_action.used = 0;
+ cache->delimits.used = 0;
+
+ {
+ controller_state_interrupt_t custom = macro_controller_state_interrupt_t_initialize(is_normal, global.thread);
+ f_state_t state = macro_f_state_t_initialize(controller_common_allocation_large_d, controller_common_allocation_small_d, 0, &controller_thread_signal_state_fss, 0, (void *) &custom, 0);
+
+ status = fl_fss_extended_content_read(cache->buffer_item, state, range, &cache->content_action, 0, &cache->delimits);
+ }
+
+ if (F_status_is_error(status)) {
+ controller_print_error(global.thread, global.main->error, F_status_set_fine(status), "fll_fss_extended_content_read", F_true);
+ }
+ else if (status == F_fss_found_content) {
+ status = fl_fss_apply_delimit(cache->delimits, &cache->buffer_item);
+
+ if (F_status_is_error(status)) {
+ controller_print_error(global.thread, global.main->error, F_status_set_fine(status), "fl_fss_apply_delimit", F_true);
+ }
+ else if (type == controller_rule_action_type_pid_file_e) {
+ item->pid_file.used = 0;
+
+ status = fl_string_dynamic_rip(cache->buffer_item, cache->content_action.array[0], &item->pid_file);
+
+ if (F_status_is_error(status)) {
+ controller_print_error(global.thread, global.main->error, F_status_set_fine(status), "fl_string_dynamic_rip", F_true);
+ }
+ else {
+ status = f_string_dynamic_terminate_after(&item->pid_file);
+
+ if (F_status_is_error(status)) {
+ controller_print_error(global.thread, global.main->error, F_status_set_fine(status), "f_string_dynamic_terminate_after", F_true);
+ }
+ }
+ }
+ else if (type == controller_rule_action_type_rerun_e) {
+ uint8_t type_rerun = 0;
+
+ if (cache->content_action.used) {
+ if (fl_string_dynamic_partial_compare_string(controller_freeze_s, cache->buffer_item, controller_freeze_s_length, cache->content_action.array[0]) == F_equal_to) {
+ type_rerun = controller_rule_action_type_execute_freeze_e;
+ }
+ if (fl_string_dynamic_partial_compare_string(controller_kill_s, cache->buffer_item, controller_kill_s_length, cache->content_action.array[0]) == F_equal_to) {
+ type_rerun = controller_rule_action_type_execute_kill_e;
+ }
+ else if (fl_string_dynamic_partial_compare_string(controller_pause_s, cache->buffer_item, controller_pause_s_length, cache->content_action.array[0]) == F_equal_to) {
+ type_rerun = controller_rule_action_type_execute_pause_e;
+ }
+ else if (fl_string_dynamic_partial_compare_string(controller_reload_s, cache->buffer_item, controller_reload_s_length, cache->content_action.array[0]) == F_equal_to) {
+ type_rerun = controller_rule_action_type_execute_reload_e;
+ }
+ else if (fl_string_dynamic_partial_compare_string(controller_restart_s, cache->buffer_item, controller_restart_s_length, cache->content_action.array[0]) == F_equal_to) {
+ type_rerun = controller_rule_action_type_execute_restart_e;
+ }
+ else if (fl_string_dynamic_partial_compare_string(controller_resume_s, cache->buffer_item, controller_resume_s_length, cache->content_action.array[0]) == F_equal_to) {
+ type_rerun = controller_rule_action_type_execute_resume_e;
+ }
+ else if (fl_string_dynamic_partial_compare_string(controller_start_s, cache->buffer_item, controller_start_s_length, cache->content_action.array[0]) == F_equal_to) {
+ type_rerun = controller_rule_action_type_execute_start_e;
+ }
+ else if (fl_string_dynamic_partial_compare_string(controller_stop_s, cache->buffer_item, controller_stop_s_length, cache->content_action.array[0]) == F_equal_to) {
+ type_rerun = controller_rule_action_type_execute_stop_e;
+ }
+ else if (fl_string_dynamic_partial_compare_string(controller_thaw_s, cache->buffer_item, controller_thaw_s_length, cache->content_action.array[0]) == F_equal_to) {
+ type_rerun = controller_rule_action_type_execute_thaw_e;
+ }
+ }
+
+ if (!type_rerun) {
+ if (global.main->error.verbosity != f_console_verbosity_quiet_e) {
+ controller_lock_print(global.main->error.to, global.thread);
+
+ fl_print_format("%c%[%SRule item action '%]", global.main->error.to.stream, f_string_eol_s[0], global.main->error.context, global.main->error.prefix, global.main->error.context);
+ fl_print_format("%[%s%]", global.main->error.to.stream, global.main->error.notable, controller_rerun_s, global.main->error.notable);
+ fl_print_format("%[' has '%]", global.main->error.to.stream, global.main->error.context, global.main->error.context);
+ fl_print_format("%[%/Q%]", global.main->error.to.stream, global.main->error.notable, cache->buffer_item, cache->content_action.array[0], global.main->error.notable);
+ fl_print_format("%[' as the first value, only the following are allowed: '%]", global.main->error.to.stream, global.main->error.context, global.main->error.context);
+ fl_print_format("%[%s%]%[', '%]", global.main->error.to.stream, global.main->error.notable, controller_freeze_s, global.main->error.notable, global.main->error.context, global.main->error.context);
+ fl_print_format("%[%s%]%[', '%]", global.main->error.to.stream, global.main->error.notable, controller_kill_s, global.main->error.notable, global.main->error.context, global.main->error.context);
+ fl_print_format("%[%s%]%[', '%]", global.main->error.to.stream, global.main->error.notable, controller_pause_s, global.main->error.notable, global.main->error.context, global.main->error.context);
+ fl_print_format("%[%s%]%[', '%]", global.main->error.to.stream, global.main->error.notable, controller_reload_s, global.main->error.notable, global.main->error.context, global.main->error.context);
+ fl_print_format("%[%s%]%[', '%]", global.main->error.to.stream, global.main->error.notable, controller_restart_s, global.main->error.notable, global.main->error.context, global.main->error.context);
+ fl_print_format("%[%s%]%[', '%]", global.main->error.to.stream, global.main->error.notable, controller_resume_s, global.main->error.notable, global.main->error.context, global.main->error.context);
+ fl_print_format("%[%s%]%[', '%]", global.main->error.to.stream, global.main->error.notable, controller_start_s, global.main->error.notable, global.main->error.context, global.main->error.context);
+ fl_print_format("%[%s%]%[', or '%]", global.main->error.to.stream, global.main->error.notable, controller_stop_s, global.main->error.notable, global.main->error.context, global.main->error.context);
+ fl_print_format("%[%s%]", global.main->error.to.stream, global.main->error.notable, controller_thaw_s, global.main->error.notable, global.main->error.context);
+ fl_print_format("%['.%]%c", global.main->error.to.stream, global.main->error.context, global.main->error.context, f_string_eol_s[0]);
+
+ controller_rule_print_error_cache(global.main->error, cache->action, F_true);
+
+ controller_unlock_print_flush(global.main->error.to, global.thread);
+ }
+
+ return F_status_set_error(F_valid_not);
+ }
+
+ controller_rule_rerun_item_t *rerun_item = 0;
+
+ if (cache->content_action.used > 1) {
+ if (fl_string_dynamic_partial_compare_string(controller_failure_s, cache->buffer_item, controller_failure_s_length, cache->content_action.array[1]) == F_equal_to) {
+ rerun_item = &item->reruns[type_rerun].failure;
+ item->reruns[type_rerun].is |= controller_rule_rerun_is_failure_d;
+ }
+ else if (fl_string_dynamic_partial_compare_string(controller_success_s, cache->buffer_item, controller_success_s_length, cache->content_action.array[1]) == F_equal_to) {
+ rerun_item = &item->reruns[type_rerun].success;
+ item->reruns[type_rerun].is |= controller_rule_rerun_is_success_d;
+ }
+ }
+ else {
+ if (global.main->error.verbosity != f_console_verbosity_quiet_e) {
+ controller_lock_print(global.main->error.to, global.thread);
+
+ fl_print_format("%c%[%SRule item action '%]", global.main->error.to.stream, f_string_eol_s[0], global.main->error.context, global.main->error.prefix, global.main->error.context);
+ fl_print_format("%[%s%]", global.main->error.to.stream, global.main->error.notable, controller_rerun_s, global.main->error.notable);
+ fl_print_format("%[' has '%]", global.main->error.to.stream, global.main->error.context, global.main->error.context);
+ fl_print_format("%[%/Q%]", global.main->error.to.stream, global.main->error.notable, cache->buffer_item, cache->content_action.array[1], global.main->error.notable);
+ fl_print_format("%[' as the second value, only the following are allowed: '%]", global.main->error.to.stream, global.main->error.context, global.main->error.context);
+ fl_print_format("%[%s%]%[' or '%]", global.main->error.to.stream, global.main->error.notable, controller_stop_s, global.main->error.notable, global.main->error.context, global.main->error.context);
+ fl_print_format("%[%s%]", global.main->error.to.stream, global.main->error.notable, controller_thaw_s, global.main->error.notable, global.main->error.context);
+ fl_print_format("%['.%]%c", global.main->error.to.stream, global.main->error.context, global.main->error.context, f_string_eol_s[0]);
+
+ controller_rule_print_error_cache(global.main->error, cache->action, F_true);
+
+ controller_unlock_print_flush(global.main->error.to, global.thread);
+ }
+
+ return F_status_set_error(F_valid_not);
+ }
+
+ for (f_array_length_t i = 2; i < cache->content_action.used; ++i) {
+
+ if (fl_string_dynamic_partial_compare_string(controller_delay_s, cache->buffer_item, controller_delay_s_length, cache->content_action.array[i]) == F_equal_to) {
+ status = controller_rule_action_read_rerun_number(global, controller_delay_s, cache, &i, &rerun_item->delay);
+ }
+ else if (fl_string_dynamic_partial_compare_string(controller_max_s, cache->buffer_item, controller_max_s_length, cache->content_action.array[i]) == F_equal_to) {
+ status = controller_rule_action_read_rerun_number(global, controller_max_s, cache, &i, &rerun_item->max);
+ }
+ else if (fl_string_dynamic_partial_compare_string(controller_reset_s, cache->buffer_item, controller_reset_s_length, cache->content_action.array[i]) == F_equal_to) {
+ item->reruns[type_rerun].is |= rerun_item == &item->reruns[type_rerun].failure ? controller_rule_rerun_is_failure_reset_d : controller_rule_rerun_is_success_reset_d;
+ }
+ else {
+ if (global.main->error.verbosity != f_console_verbosity_quiet_e) {
+ controller_lock_print(global.main->error.to, global.thread);
+
+ fl_print_format("%c%[%SRule item action '%]", global.main->error.to.stream, f_string_eol_s[0], global.main->error.context, global.main->error.prefix, global.main->error.context);
+ fl_print_format("%[%s%]", global.main->error.to.stream, global.main->error.notable, controller_rerun_s, global.main->error.notable);
+ fl_print_format("%[' has an unknown value '%]", global.main->error.to.stream, global.main->error.context, global.main->error.context);
+ fl_print_format("%[%/Q%]", global.main->error.to.stream, global.main->error.notable, cache->buffer_item, cache->content_action.array[i], global.main->error.notable);
+ fl_print_format("%['.%]%c", global.main->error.to.stream, global.main->error.context, global.main->error.context, f_string_eol_s[0]);
+
+ controller_rule_print_error_cache(global.main->error, cache->action, F_true);
+
+ controller_unlock_print_flush(global.main->error.to, global.thread);
+ }
+
+ return F_status_set_error(F_valid_not);
+ }
+ } // for
+ }
+ else if (type == controller_rule_action_type_with_e) {
+
+ for (f_array_length_t i = 0; i < cache->content_action.used; ++i) {
+
+ if (fl_string_dynamic_partial_compare_string(controller_full_path_s, cache->buffer_item, controller_full_path_s_length, cache->content_action.array[i]) == F_equal_to) {
+ item->with |= controller_with_full_path_d;
+ }
+ else if (fl_string_dynamic_partial_compare_string(controller_session_new_s, cache->buffer_item, controller_session_new_s_length, cache->content_action.array[i]) == F_equal_to) {
+ item->with |= controller_with_session_new_d;
+
+ // "session_new" and "session_same" are mutually exclusive.
+ if (item->with & controller_with_session_same_d) {
+ item->with -= controller_with_session_same_d;
+ }
+ }
+ else if (fl_string_dynamic_partial_compare_string(controller_session_same_s, cache->buffer_item, controller_session_same_s_length, cache->content_action.array[i]) == F_equal_to) {
+ item->with |= controller_with_session_same_d;
+
+ // "session_new" and "session_same" are mutually exclusive.
+ if (item->with & controller_with_session_new_d) {
+ item->with -= controller_with_session_new_d;
+ }
+ }
+ else {
+ if (global.main->error.verbosity != f_console_verbosity_quiet_e) {
+ controller_lock_print(global.main->error.to, global.thread);
+
+ fl_print_format("%c%[%SUnknown value '%]", global.main->error.to.stream, f_string_eol_s[0], global.main->error.context, global.main->error.prefix, global.main->error.context);
+ fl_print_format("%[%/Q%]", global.main->error.to.stream, global.main->error.notable, cache->buffer_item, cache->content_action.array[i], global.main->error.notable);
+ fl_print_format("%[' for rule item action '%]%[%s%]", global.main->error.to.stream, global.main->error.context, global.main->error.context, global.main->error.notable, controller_with_s, global.main->error.notable);
+ fl_print_format("%['.%]%c", global.main->error.to.stream, global.main->error.context, global.main->error.context, f_string_eol_s[0]);
+
+ controller_rule_print_error_cache(global.main->error, cache->action, F_true);
+
+ controller_unlock_print_flush(global.main->error.to, global.thread);
+ }
+
+ status = F_status_set_error(F_valid_not);
+ break;
+ }
+ } // for
+ }
+ else if (item->type == controller_rule_item_type_script_e || item->type == controller_rule_item_type_utility_e) {
+ status = f_string_dynamics_increase(controller_common_allocation_small_d, &actions->array[actions->used].parameters);
+
+ if (F_status_is_error(status)) {
+ controller_print_error(global.thread, global.main->error, F_status_set_fine(status), "f_string_dynamics_increase", F_true);
+ }
+ else {
+
+ // "script" types use the entire content as a single string piped to the script, so merge all arguments together.
+ actions->array[actions->used].type = type;
+ actions->array[actions->used].line = cache->action.line_action;
+ actions->array[actions->used].parameters.used = 0;
+ actions->array[actions->used].status = F_known_not;
+
+ for (f_array_length_t i = 0; i < cache->content_action.used; ++i) {
+
+ status = f_string_dynamic_partial_mash_nulless(f_string_space_s, F_string_space_s_length, cache->buffer_item, cache->content_action.array[i], &actions->array[actions->used].parameters.array[0]);
+ if (F_status_is_error(status)) break;
+ } // for
+
+ if (F_status_is_error(status)) {
+ controller_print_error(global.thread, global.main->error, F_status_set_fine(status), "f_string_dynamic_partial_mash_nulless", F_true);
+ }
+ else {
+ status = f_string_dynamic_terminate_after(&actions->array[actions->used].parameters.array[0]);
+
+ if (F_status_is_error(status)) {
+ controller_print_error(global.thread, global.main->error, F_status_set_fine(status), "f_string_dynamic_terminate_after", F_true);
+ }
+ else {
+ actions->array[actions->used++].parameters.used = 1;
+ }
+ }
+ }
+ }
+ else {
+ status = f_fss_count_lines(cache->buffer_item, range->start, &actions->array[actions->used].line);
+
+ if (F_status_is_error(status)) {
+ controller_print_error(global.thread, global.main->error, F_status_set_fine(status), "f_fss_count_lines", F_true);
+ }
+ else {
+ actions->array[actions->used].type = type;
+ actions->array[actions->used].line += ++item->line;
+ actions->array[actions->used].parameters.used = 0;
+ actions->array[actions->used].status = F_known_not;
+
+ status = controller_rule_parameters_read(global, cache->buffer_item, 0, &cache->content_action, &actions->array[actions->used].parameters);
+
+ if (F_status_is_error(status)) {
+ controller_print_error(global.thread, global.main->error, F_status_set_fine(status), "controller_rule_parameters_read", F_true);
+
+ actions->array[actions->used++].status = controller_status_simplify_error(F_status_set_fine(status));
+ }
+ else {
+ actions->array[actions->used++].status = status;
+ }
+ }
+ }
+ }
+ else {
+ status = F_data_not;
+ }
+ }
+
+ if (F_status_is_error_not(status) && status == F_data_not) {
+ if (global.main->warning.verbosity == f_console_verbosity_debug_e) {
+ controller_lock_print(global.main->warning.to, global.thread);
+
+ fl_print_format("%c%[%SAction is empty, nothing to do.%]%c", global.main->warning.to.stream, f_string_eol_s[0], global.main->warning.context, global.main->warning.prefix, global.main->warning.context, f_string_eol_s[0]);
+
+ controller_rule_print_error_cache(global.main->warning, cache->action, F_true);
+
+ controller_unlock_print_flush(global.main->warning.to, global.thread);
+ }
+ }
+
+ return status;
+ }
+#endif // _di_controller_rule_action_read_
+
+#ifndef _di_controller_rule_action_read_rerun_number_
+ f_status_t controller_rule_action_read_rerun_number(const controller_global_t global, const f_string_t name, controller_cache_t * const cache, f_array_length_t * const index, f_number_unsigned_t * const number) {
+
+ f_status_t status = F_none;
+ f_number_unsigned_t parsed = 0;
+
+ if (*index + 1 == cache->content_action.used) {
+ status = F_status_set_error(F_valid_not);
+ }
+ else {
+ status = fl_conversion_string_to_number_signed(cache->buffer_item.string, cache->content_action.array[++(*index)], &parsed);
+
+ if (F_status_set_fine(status) == F_number_positive) {
+ status = fl_conversion_string_to_number_signed(cache->buffer_item.string, controller_range_after_number_sign(cache->buffer_item, cache->content_action.array[*index]), &parsed);
+ }
+
+ if (status == F_data_not) {
+ status = F_status_set_error(F_valid_not);
+ }
+ }
+
+ if (F_status_is_error(status)) {
+ if (global.main->error.verbosity != f_console_verbosity_quiet_e) {
+ status = F_status_set_fine(status);
+
+ if (status != F_valid_not && status != F_number && status != F_number_decimal && status != F_number_overflow && status != F_number_underflow && status != F_number_negative) {
+ controller_print_error(global.thread, global.main->error, F_status_set_fine(status), "f_string_dynamics_increase", F_true);
+ }
+ else {
+ controller_lock_print(global.main->error.to, global.thread);
+
+ fl_print_format("%c%[%SRule item action '%]", global.main->error.to.stream, f_string_eol_s[0], global.main->error.context, global.main->error.prefix, global.main->error.context);
+ fl_print_format("%[%s%]", global.main->error.to.stream, global.main->error.notable, controller_rerun_s, global.main->error.notable);
+ fl_print_format("%[' requires a positive whole number or 0 for the '%]", global.main->error.to.stream, global.main->error.context, global.main->error.context);
+ fl_print_format("%[%S%]", global.main->error.to.stream, global.main->error.notable, name, global.main->error.notable);
+ fl_print_format("%[' value", global.main->error.to.stream, global.main->error.context, global.main->error.context);
+
+ if (*index + 1 == cache->content_action.used) {
+ fl_print_format(", but none were given.%]%c", global.main->error.to.stream, global.main->error.context, f_string_eol_s[0]);
+ }
+ else {
+ fl_print_format(", but '%]%[%/Q%]", global.main->error.to.stream, global.main->error.context, global.main->error.notable, cache->buffer_item, cache->content_action.array[*index], global.main->error.notable);
+
+ if (status == F_number || status == F_number_decimal) {
+ fl_print_format("%[' was given.%]%c", global.main->error.to.stream, global.main->error.context, global.main->error.context, f_string_eol_s[0]);
+ }
+ else if (status == F_number_overflow) {
+ fl_print_format("%[' is too large.%]%c", global.main->error.to.stream, global.main->error.context, global.main->error.context, f_string_eol_s[0]);
+ }
+ else {
+ fl_print_format("%[' is negative.%]%c", global.main->error.to.stream, global.main->error.context, global.main->error.context, f_string_eol_s[0]);
+ }
+ }
+
+ controller_rule_print_error_cache(global.main->error, cache->action, F_true);
+
+ controller_unlock_print_flush(global.main->error.to, global.thread);
+ }
+ }
+
+ return status;
+ }
+
+ *number = parsed;
+
+ return F_none;
+ }
+#endif // _di_controller_rule_action_read_rerun_number_
+
+#ifndef _di_controller_rule_copy_
+ f_status_t controller_rule_copy(const controller_rule_t source, controller_rule_t *destination) {
+
+ f_status_t status = F_none;
+
+ // Delete the third party structures.
+ macro_f_control_group_t_delete_simple(destination->cgroup)
+ f_capability_delete(&destination->capability);
+
+ for (f_array_length_t i = 0; i < controller_rule_action_type__enum_size_e; ++i) {
+ destination->status[i] = source.status[i];
+ } // for
+
+ destination->timeout_kill = source.timeout_kill;
+ destination->timeout_start = source.timeout_start;
+ destination->timeout_stop = source.timeout_stop;
+
+ destination->has = source.has;
+ destination->nice = source.nice;
+ destination->user = source.user;
+ destination->group = source.group;
+
+ destination->timestamp.seconds = source.timestamp.seconds;
+ destination->timestamp.nanoseconds = source.timestamp.nanoseconds;
+
+ destination->alias.used = 0;
+ destination->name.used = 0;
+ destination->path.used = 0;
+ destination->script.used = 0;
+
+ destination->define.used = 0;
+ destination->parameter.used = 0;
+ destination->environment.used = 0;
+
+ destination->affinity.used = 0;
+ destination->groups.used = 0;
+ destination->limits.used = 0;
+ destination->scheduler.policy = source.scheduler.policy;
+ destination->scheduler.priority = source.scheduler.priority;
+
+ for (f_array_length_t i = 0; i < destination->ons.size; ++i) {
+
+ destination->ons.array[i].action = 0;
+ destination->ons.array[i].need.used = 0;
+ destination->ons.array[i].want.used = 0;
+ destination->ons.array[i].wish.used = 0;
+ } // for
+
+ destination->ons.used = 0;
+ destination->items.used = 0;
+
+ if (source.alias.used) {
+ status = f_string_dynamic_append(source.alias, &destination->alias);
+ if (F_status_is_error(status)) return status;
+ }
+
+ if (source.name.used) {
+ status = f_string_dynamic_append(source.name, &destination->name);
+ if (F_status_is_error(status)) return status;
+ }
+
+ if (source.path.used) {
+ status = f_string_dynamic_append(source.path, &destination->path);
+ if (F_status_is_error(status)) return status;
+ }
+
+ if (source.script.used) {
+ status = f_string_dynamic_append(source.script, &destination->script);
+ if (F_status_is_error(status)) return status;
+ }
+
+ if (source.define.used) {
+ status = f_string_maps_append(source.define, &destination->define);
+ if (F_status_is_error(status)) return status;
+ }
+
+ if (source.parameter.used) {
+ status = f_string_maps_append(source.parameter, &destination->parameter);
+ if (F_status_is_error(status)) return status;
+ }
+
+ if (source.environment.used) {
+ status = f_string_dynamics_append(source.environment, &destination->environment);
+ if (F_status_is_error(status)) return status;
+ }
+
+ if (source.ons.used) {
+ if (destination->ons.used < source.ons.used) {
+ status = controller_rule_ons_resize(source.ons.used, &destination->ons);
+ if (F_status_is_error(status)) return status;
+ }
+
+ for (f_array_length_t i = 0; i < source.ons.used; ++i) {
+
+ destination->ons.array[i].action = source.ons.array[i].action;
+
+ if (source.ons.array[i].need.used) {
+ destination->ons.array[i].need.used = 0;
+
+ status = f_string_dynamics_append(source.ons.array[i].need, &destination->ons.array[i].need);
+ if (F_status_is_error(status)) return status;
+ }
+
+ if (source.ons.array[i].want.used) {
+ destination->ons.array[i].want.used = 0;
+
+ status = f_string_dynamics_append(source.ons.array[i].want, &destination->ons.array[i].want);
+ if (F_status_is_error(status)) return status;
+ }
+
+ if (source.ons.array[i].wish.used) {
+ destination->ons.array[i].wish.used = 0;
+
+ status = f_string_dynamics_append(source.ons.array[i].wish, &destination->ons.array[i].wish);
+ if (F_status_is_error(status)) return status;
+ }
+ } // for
+
+ destination->ons.used = source.ons.used;
+ }
+
+ if (source.affinity.used) {
+ status = f_type_int32s_append(source.affinity, &destination->affinity);
+ if (F_status_is_error(status)) return status;
+ }
+
+ if (source.capability) {
+ status = f_capability_copy(source.capability, &destination->capability);
+ if (F_status_is_error(status)) return status;
+ }
+
+ status = f_control_group_copy(source.cgroup, &destination->cgroup);
+ if (F_status_is_error(status)) return status;
+
+ if (source.groups.used) {
+ status = f_type_int32s_append(source.groups, &destination->groups);
+ if (F_status_is_error(status)) return status;
+ }
+
+ if (source.limits.used) {
+ status = f_limit_sets_copy(source.limits, &destination->limits);
+ if (F_status_is_error(status)) return status;
+ }
+
+ if (source.items.used) {
+ controller_rule_item_t *item_source = 0;
+ controller_rule_item_t *item_destination = 0;
+
+ controller_rule_action_t *action_source = 0;
+ controller_rule_action_t *action_destination = 0;
+
+ if (source.items.used > destination->items.size) {
+ status = controller_rule_items_increase_by(source.items.used - destination->items.size, &destination->items);
+ if (F_status_is_error(status)) return status;
+ }
+
+ f_array_length_t i = 0;
+ f_array_length_t j = 0;
+
+ for (; i < source.items.used; ++i) {
+
+ item_source = &source.items.array[i];
+ item_destination = &destination->items.array[i];
+
+ if (item_source->actions.used > item_destination->actions.size) {
+ status = controller_rule_actions_increase_by(item_source->actions.used - item_destination->actions.size, &item_destination->actions);
+ if (F_status_is_error(status)) return status;
+ }
+
+ item_destination->type = item_source->type;
+ item_destination->with = item_source->with;
+ item_destination->line = item_source->line;
+ item_destination->pid_file.used = 0;
+
+ status = f_string_dynamic_append(item_source->pid_file, &item_destination->pid_file);
+ if (F_status_is_error(status)) return status;
+
+ status = f_string_dynamic_terminate_after(&item_destination->pid_file);
+ if (F_status_is_error(status)) return status;
+
+ for (j = 0; j < controller_rule_action_type_execute__enum_size_e; ++j) {
+ item_destination->reruns[j].is = item_source->reruns[j].is;
+ item_destination->reruns[j].failure.count = item_source->reruns[j].failure.count;
+ item_destination->reruns[j].failure.delay = item_source->reruns[j].failure.delay;
+ item_destination->reruns[j].failure.max = item_source->reruns[j].failure.max;
+ item_destination->reruns[j].success.count = item_source->reruns[j].success.count;
+ item_destination->reruns[j].success.delay = item_source->reruns[j].success.delay;
+ item_destination->reruns[j].success.max = item_source->reruns[j].success.max;
+ } // for
+
+ for (j = 0; j < item_source->actions.used; ++j) {
+
+ action_source = &item_source->actions.array[j];
+ action_destination = &item_destination->actions.array[j];
+
+ action_destination->type = action_source->type;
+ action_destination->line = action_source->line;
+ action_destination->status = action_source->status;
+
+ action_destination->parameters.used = 0;
+
+ status = f_string_dynamics_append(action_source->parameters, &action_destination->parameters);
+ if (F_status_is_error(status)) return status;
+ } // for
+
+ item_destination->actions.used = item_source->actions.used;
+ } // for
+
+ destination->items.used = source.items.used;
+ }
+
+ return status;
+ }
+#endif // _di_controller_rule_copy_
+
+#ifndef _di_controller_rule_execute_
+ f_status_t controller_rule_execute(const controller_global_t global, const uint8_t action, const uint8_t options, controller_process_t * const process) {
+
+ f_status_t status = F_none;
+ f_status_t success = F_false;
+
+ f_array_length_t i = 0;
+ f_array_length_t j = 0;
+ f_array_length_t k = 0;
+
+ // Child processes should receive all signals and handle the signals as they see fit.
+ f_signal_how_t signals = f_signal_how_t_initialize;
+ f_signal_set_empty(&signals.block);
+ f_signal_set_fill(&signals.block_not);
+
+ f_string_maps_t environment = f_string_maps_t_initialize;
+
+ const f_string_dynamics_t arguments_none = f_string_dynamics_t_initialize;
+
+ controller_execute_set_t execute_set = macro_controller_execute_set_t_initialize(0, 0, process->rule.has & controller_rule_has_environment_d ? &environment : 0, &signals, 0, fl_execute_as_t_initialize);
+
+ if (process->rule.affinity.used) {
+ execute_set.as.affinity = &process->rule.affinity;
+ }
+
+ if (process->rule.capability) {
+ execute_set.as.capability = process->rule.capability;
+ }
+
+ if (process->rule.has & controller_rule_has_cgroup_d) {
+ execute_set.as.control_group = &process->rule.cgroup;
+
+ // Make sure all required cgroup directories exist.
+ if (controller_rule_status_is_available(action, process->rule)) {
+ status = fll_control_group_prepare(process->rule.cgroup);
+
+ if (F_status_is_error(status)) {
+ controller_print_error_file(global.thread, global.main->error, F_status_set_fine(status), "fll_control_group_prepare", F_true, process->rule.cgroup.path.string, "prepare control groups for", fll_error_file_type_directory_e);
+
+ return status;
+ }
+ }
+ }
+
+ if (process->rule.has & controller_rule_has_group_d) {
+ execute_set.as.id_group = &process->rule.group;
+
+ if (process->rule.groups.used) {
+ execute_set.as.id_groups = &process->rule.groups;
+ }
+ }
+
+ if (process->rule.limits.used) {
+ execute_set.as.limits = &process->rule.limits;
+ }
+
+ if (process->rule.has & controller_rule_has_scheduler_d) {
+ execute_set.as.scheduler = &process->rule.scheduler;
+ }
+
+ if (process->rule.has & controller_rule_has_nice_d) {
+ execute_set.as.nice = &process->rule.nice;
+ }
+
+ if (process->rule.has & controller_rule_has_user_d) {
+ execute_set.as.id_user = &process->rule.user;
+ }
+
+ status = fl_environment_load_names(process->rule.environment, &environment);
+
+ if (F_status_is_error(status)) {
+ controller_print_error(global.thread, global.main->error, F_status_set_fine(status), "fl_environment_load_names", F_true);
+
+ return status;
+ }
+
+ for (i = 0; i < process->rule.items.used && controller_thread_is_enabled_process(process, global.thread); ++i) {
+
+ if (process->rule.items.array[i].type == controller_rule_item_type_setting_e) continue;
+
+ for (j = 0; j < process->rule.items.array[i].actions.used; ++j) {
+
+ if (!controller_thread_is_enabled_process(process, global.thread)) {
+ status = F_status_set_error(F_interrupt);
+
+ break;
+ }
+
+ if (process->rule.items.array[i].actions.array[j].type != action) continue;
+
+ execute_set.parameter.data = 0;
+ execute_set.parameter.option = FL_execute_parameter_option_threadsafe_d | FL_execute_parameter_option_return_d;
+
+ if (process->rule.items.array[i].with & controller_with_full_path_d) {
+ execute_set.parameter.option |= FL_execute_parameter_option_path_d;
+ }
+
+ if (process->rule.items.array[i].with & controller_with_session_new_d) {
+ execute_set.parameter.option |= FL_execute_parameter_option_session_d;
+ }
+
+ if (process->rule.items.array[i].type == controller_rule_item_type_command_e) {
+ for (;;) {
+
+ status = controller_rule_execute_foreground(process->rule.items.array[i].type, 0, process->rule.items.array[i].actions.array[j].parameters, options, &execute_set, process);
+
+ if (status == F_child || F_status_set_fine(status) == F_interrupt || F_status_set_fine(status) == F_lock) break;
+ if (F_status_is_error(status) && F_status_set_fine(status) != F_failure) break;
+
+ if (controller_rule_execute_rerun(controller_rule_action_type_to_action_execute_type(action), process, &process->rule.items.array[i]) > 0) {
+ continue;
+ }
+
+ break;
+ } // for
+
+ if (status == F_child || F_status_set_fine(status) == F_interrupt || F_status_set_fine(status) == F_lock) break;
+
+ if (F_status_is_error(status)) {
+ process->rule.items.array[i].actions.array[j].status = F_status_set_error(F_failure);
+
+ if (!(options & controller_process_option_simulate_d)) break;
+
+ success = F_status_set_error(F_failure);
+ }
+ else if (success == F_false || success == F_ignore) {
+ success = F_true;
+ }
+ }
+ else if (process->rule.items.array[i].type == controller_rule_item_type_script_e) {
+ execute_set.parameter.data = &process->rule.items.array[i].actions.array[j].parameters.array[0];
+
+ for (;;) {
+
+ status = controller_rule_execute_foreground(process->rule.items.array[i].type, process->rule.script.used ? process->rule.script.string : controller_default_program_script_s, arguments_none, options, &execute_set, process);
+
+ if (status == F_child || F_status_set_fine(status) == F_lock) break;
+ if (F_status_is_error(status) && F_status_set_fine(status) != F_failure) break;
+
+ if (controller_rule_execute_rerun(controller_rule_action_type_to_action_execute_type(action), process, &process->rule.items.array[i]) > 0) {
+ continue;
+ }
+
+ break;
+ } // for
+
+ if (status == F_child || F_status_set_fine(status) == F_interrupt || F_status_set_fine(status) == F_lock) break;
+
+ if (F_status_is_error(status)) {
+ process->rule.items.array[i].actions.array[j].status = F_status_set_error(F_failure);
+
+ if (!(options & controller_process_option_simulate_d)) break;
+
+ success = F_status_set_error(F_failure);
+ }
+ else if (success == F_false || success == F_ignore) {
+ success = F_true;
+ }
+ }
+ else if (process->rule.items.array[i].type == controller_rule_item_type_service_e) {
+ if (process->rule.items.array[i].pid_file.used) {
+ for (;;) {
+
+ status = controller_rule_execute_pid_with(process->rule.items.array[i].pid_file, process->rule.items.array[i].type, 0, process->rule.items.array[i].actions.array[j].parameters, options, process->rule.items.array[i].with, &execute_set, process);
+
+ if (status == F_child || F_status_set_fine(status) == F_interrupt || F_status_set_fine(status) == F_lock) break;
+ if (F_status_is_error(status) && F_status_set_fine(status) != F_failure) break;
+
+ if (controller_rule_execute_rerun(controller_rule_action_type_to_action_execute_type(action), process, &process->rule.items.array[i]) > 0) {
+ continue;
+ }
+
+ break;
+ } // for
+
+ if (status == F_child || F_status_set_fine(status) == F_interrupt || F_status_set_fine(status) == F_lock) break;
+
+ if (F_status_is_error(status)) {
+ process->rule.items.array[i].actions.array[j].status = F_status_set_error(F_failure);
+
+ if (!(options & controller_process_option_simulate_d)) break;
+
+ success = F_status_set_error(F_failure);
+ }
+ else if (success == F_false || success == F_ignore) {
+ success = F_true;
+ }
+ }
+ else {
+ success = F_status_set_error(F_failure);
+
+ // @todo make this more specific.
+ controller_rule_action_print_error_missing_pid(global.main->error, process->rule.alias.string);
+ }
+ }
+ else if (process->rule.items.array[i].type == controller_rule_item_type_utility_e) {
+ if (process->rule.items.array[i].pid_file.used) {
+ execute_set.parameter.data = &process->rule.items.array[i].actions.array[j].parameters.array[0];
+
+ for (;;) {
+
+ status = controller_rule_execute_pid_with(process->rule.items.array[i].pid_file, process->rule.items.array[i].type, process->rule.script.used ? process->rule.script.string : controller_default_program_script_s, arguments_none, options, process->rule.items.array[i].with, &execute_set, process);
+
+ if (status == F_child || F_status_set_fine(status) == F_interrupt || F_status_set_fine(status) == F_lock) break;
+ if (F_status_is_error(status) && F_status_set_fine(status) != F_failure) break;
+
+ if (controller_rule_execute_rerun(controller_rule_action_type_to_action_execute_type(action), process, &process->rule.items.array[i]) > 0) {
+ continue;
+ }
+
+ break;
+ } // for
+
+ if (status == F_child || F_status_set_fine(status) == F_interrupt || F_status_set_fine(status) == F_lock) break;
+
+ if (F_status_is_error(status)) {
+ process->rule.items.array[i].actions.array[j].status = F_status_set_error(F_failure);
+
+ if (!(options & controller_process_option_simulate_d)) break;
+
+ success = F_status_set_error(F_failure);
+ }
+ else if (success == F_false || success == F_ignore) {
+ success = F_true;
+ }
+ }
+ else {
+ success = F_status_set_error(F_failure);
+
+ // @todo make this more specific.
+ controller_rule_action_print_error_missing_pid(global.main->error, process->rule.alias.string);
+ }
+ }
+ else {
+ if (global.main->warning.verbosity == f_console_verbosity_debug_e) {
+ controller_lock_print(global.main->warning.to, global.thread);
+
+ fl_print_format("%c%[%SAction type is unknown, ignoring.%]%c", global.main->warning.to.stream, f_string_eol_s[0], global.main->warning.context, global.main->warning.prefix, global.main->warning.context, f_string_eol_s[0]);
+
+ controller_rule_print_error_cache(global.main->warning, process->cache.action, F_true);
+
+ controller_unlock_print_flush(global.main->warning.to, global.thread);
+ }
+
+ if (success == F_false) {
+ success = F_ignore;
+ }
+
+ continue;
+ }
+ } // for
+
+ if (status == F_child || F_status_set_fine(status) == F_interrupt || F_status_is_error(status) && !(options & controller_process_option_simulate_d)) {
+ break;
+ }
+ } // for
+
+ macro_f_string_maps_t_delete_simple(environment);
+
+ // Lock failed, attempt to re-establish lock before returning.
+ if (F_status_set_fine(status) == F_lock) {
+ status = controller_lock_read(process, global.thread, &process->lock);
+
+ if (F_status_is_error(status)) {
+ return F_status_set_error(F_lock);
+ }
+
+ success = F_false;
+ }
+
+ if (!controller_thread_is_enabled_process(process, global.thread)) {
+ return F_status_set_error(F_interrupt);
+ }
+
+ if (status == F_child || F_status_is_error(status)) {
+ return status;
+ }
+
+ if (success == F_false || success == F_failure) {
+ return F_status_set_error(F_failure);
+ }
+
+ if (success == F_ignore) {
+ return F_ignore;
+ }
+
+ return F_none;
+ }
+#endif // _di_controller_rule_execute_
+
+#ifndef _di_controller_rule_execute_foreground_
+ f_status_t controller_rule_execute_foreground(const uint8_t type, const f_string_t program, const f_string_statics_t arguments, const uint8_t options, controller_execute_set_t * const execute_set, controller_process_t * const process) {
+
+ f_status_t status = F_none;
+ f_status_t status_lock = F_none;
+
+ f_string_dynamics_t arguments_none = f_string_dynamics_t_initialize;
+
+ controller_main_t * const main = (controller_main_t *) process->main_data;
+ controller_thread_t * const thread = (controller_thread_t *) process->main_thread;
+
+ f_execute_result_t result = f_execute_result_t_initialize;
+
+ status = controller_pids_increase(&process->childs);
+
+ if (F_status_is_error(status)) {
+ controller_print_error(thread, main->error, F_status_set_fine(status), "controller_pids_increase", F_true);
+
+ return status;
+ }
+
+ pid_t *child = 0;
+ f_string_dynamic_t *child_pid_file = 0;
+
+ {
+ f_array_length_t i = 0;
+
+ while (i < process->childs.used && process->childs.array[i]) {
+ ++i;
+ } // while
+
+ child = &process->childs.array[i];
+
+ if (i == process->childs.used) {
+ ++process->childs.used;
+ }
+ }
+
+ if (options & controller_process_option_simulate_d) {
+ if (main->output.verbosity != f_console_verbosity_quiet_e) {
+ controller_lock_print(main->output.to, thread);
+
+ fl_print_format("%cSimulating execution of '%[", main->output.to.stream, f_string_eol_s[0], main->context.set.title);
+
+ if (program) {
+ f_print_safely_terminated(program, main->output.to.stream);
+ }
+ else {
+ f_print_dynamic_safely(arguments.array[0], main->output.to.stream);
+ }
+
+ fl_print_format("%]' with the arguments: '%[", main->output.to.stream, main->context.set.title, main->context.set.important);
+
+ for (f_array_length_t i = program ? 0 : 1; i < arguments.used; ++i) {
+
+ if (program && i || !program && i > 1) {
+ f_print_terminated(f_string_space_s, main->output.to.stream);
+ }
+
+ f_print_dynamic_safely(arguments.array[i], main->output.to.stream);
+ } // for
+
+ fl_print_format("%]' from '", main->output.to.stream, main->context.set.important);
+ fl_print_format("%[%Q%]'.%c", main->output.to.stream, main->context.set.notable, process->rule.name, main->context.set.notable, f_string_eol_s[0]);
+
+ controller_unlock_print_flush(main->output.to, thread);
+ }
+
+ // Sleep for less than a second to better show simulation of synchronous vs asynchronous.
+ {
+ const struct timespec delay = controller_time_milliseconds(controller_thread_simulation_timeout_d);
+
+ if (controller_time_sleep_nanoseconds(main, (controller_setting_t *) process->main_setting, delay) == -1) {
+ status = F_status_set_error(F_interrupt);
+ }
+ }
+
+ if (F_status_set_fine(status) != F_interrupt) {
+ const f_string_static_t simulated_program = macro_f_string_static_t_initialize(f_string_empty_s, 0);
+ const f_string_statics_t simulated_arguments = f_string_statics_t_initialize;
+ fl_execute_parameter_t simulated_parameter = macro_fl_execute_parameter_t_initialize(execute_set->parameter.option, execute_set->parameter.wait, process->rule.has & controller_rule_has_environment_d ? execute_set->parameter.environment : 0, execute_set->parameter.signals, &simulated_program);
+
+ status = fll_execute_program(controller_default_program_script_s, simulated_arguments, &simulated_parameter, &execute_set->as, (void *) &result);
+ }
+ }
+ else {
+ status = fll_execute_program(program, arguments, &execute_set->parameter, &execute_set->as, (void *) &result);
+ }
+
+ if (status == F_parent) {
+ const pid_t id_child = result.pid;
+ result.status = 0;
+
+ f_thread_unlock(&process->lock);
+
+ status_lock = controller_lock_write_process(process, thread, &process->lock);
+
+ if (F_status_is_error(status_lock)) {
+ controller_lock_print_error_critical(main->error, F_status_set_fine(status_lock), F_false, thread);
+
+ if (F_status_set_fine(status_lock) != F_interrupt) {
+ status = controller_lock_read_process(process, thread, &process->lock);
+
+ if (status == F_none) {
+ return status_lock;
+ }
+ }
+
+ return F_status_set_error(F_lock);
+ }
+
+ // Assign the child process id to allow for the cancel process to send appropriate termination signals to the child process.
+ *child = id_child;
+
+ f_thread_unlock(&process->lock);
+
+ status_lock = controller_lock_read_process(process, thread, &process->lock);
+
+ if (F_status_is_error(status_lock)) {
+ controller_lock_print_error_critical(main->error, F_status_set_fine(status_lock), F_true, thread);
+ }
+
+ if (F_status_set_fine(status_lock) != F_interrupt) {
+
+ // Have the parent wait for the child process to finish.
+ waitpid(id_child, &result.status, 0);
+ }
+
+ if (F_status_set_fine(status_lock) == F_interrupt || !controller_thread_is_enabled_process(process, thread)) {
+ if (status_lock == F_none) {
+ return F_status_set_error(F_interrupt);
+ }
+
+ return F_status_set_error(F_lock);
+ }
+
+ if (status_lock == F_none) {
+ f_thread_unlock(&process->lock);
+ }
+
+ status_lock = controller_lock_write_process(process, thread, &process->lock);
+
+ if (F_status_is_error(status_lock)) {
+ controller_lock_print_error_critical(main->error, F_status_set_fine(status_lock), F_false, thread);
+
+ if (F_status_set_fine(status_lock) != F_interrupt) {
+ status = controller_lock_read_process(process, thread, &process->lock);
+
+ if (status == F_none) {
+ return status_lock;
+ }
+ }
+
+ return F_status_set_error(F_lock);
+ }
+
+ process->result = result.status;
+
+ // Remove the pid now that waidpid() has returned.
+ *child = 0;
+
+ f_thread_unlock(&process->lock);
+
+ status_lock = controller_lock_read_process(process, thread, &process->lock);
+
+ if (F_status_is_error(status_lock)) {
+ controller_lock_print_error_critical(main->error, F_status_set_fine(status_lock), F_true, thread);
+
+ return F_status_set_error(F_lock);
+ }
+
+ if (WIFEXITED(result.status) ? WEXITSTATUS(result.status) : 0) {
+ status = F_status_set_error(F_failure);
+ }
+ else {
+ status = F_none;
+ }
+ }
+ else {
+ main->child = result.status;
+
+ if (!controller_thread_is_enabled_process(process, thread)) {
+ return F_status_set_error(F_interrupt);
+ }
+ }
+
+ if (F_status_is_error(status)) {
+ status = F_status_set_fine(status);
+
+ if (status == F_child || status == F_capability || status == F_group || status == F_nice || status == F_user) {
+ status = F_child;
+ }
+ else {
+ status = F_status_set_error(status);
+ }
+ }
+
+ if (status == F_child || F_status_set_fine(status) == F_interrupt) {
+ return status;
+ }
+
+ if (F_status_is_error(status)) {
+ status = F_status_set_fine(status);
+
+ if ((WIFEXITED(process->result) && WEXITSTATUS(process->result)) || status == F_control_group || status == F_failure || status == F_limit || status == F_processor || status == F_schedule) {
+ controller_rule_item_print_error_execute(type == controller_rule_item_type_script_e, program ? program : arguments.used ? arguments.array[0].string : f_string_empty_s, status, process);
+ }
+ else {
+ controller_print_error(thread, main->error, F_status_set_fine(status), "fll_execute_program", F_true);
+ }
+
+ status = F_status_set_error(status);
+ }
+
+ return status;
+ }
+#endif // _di_controller_rule_execute_foreground_
+
+#ifndef _di_controller_rule_execute_pid_with_
+ f_status_t controller_rule_execute_pid_with(const f_string_dynamic_t pid_file, const uint8_t type, const f_string_t program, const f_string_statics_t arguments, const uint8_t options, const uint8_t with, controller_execute_set_t * const execute_set, controller_process_t * const process) {
+
+ f_status_t status = F_none;
+ f_status_t status_lock = F_none;
+
+ controller_main_t * const main = (controller_main_t *) process->main_data;
+ controller_thread_t * const thread = (controller_thread_t *) process->main_thread;
+
+ f_execute_result_t result = f_execute_result_t_initialize;
+
+ status = controller_pids_increase(&process->childs);
+
+ if (F_status_is_error(status)) {
+ controller_print_error(thread, main->error, F_status_set_fine(status), "controller_pids_increase", F_true);
+
+ return status;
+ }
+
+ status = f_string_dynamics_increase(controller_common_allocation_small_d, &process->path_pids);
+
+ if (F_status_is_error(status)) {
+ controller_print_error(thread, main->error, F_status_set_fine(status), "f_string_dynamics_increase", F_true);
+
+ return status;
+ }
+
+ pid_t *child = 0;
+ f_string_dynamic_t *child_pid_file = 0;
+
+ {
+ f_array_length_t i = 0;
+
+ while (i < process->childs.used && process->childs.array[i]) {
+ ++i;
+ } // while
+
+ child = &process->childs.array[i];
+
+ if (i == process->childs.used) {
+ ++process->childs.used;
+ }
+
+ for (i = 0; i < process->path_pids.used && process->path_pids.array[i].used; ++i) {
+ // Do nothing.
+ } // for
+
+ child_pid_file = &process->path_pids.array[i];
+
+ if (i == process->path_pids.used) {
+ ++process->path_pids.used;
+ }
+ }
+
+ status = f_file_exists(pid_file.string);
+
+ if (F_status_is_error(status)) {
+ controller_print_error_file(thread, main->error, F_status_set_fine(status), "f_file_exists", F_true, pid_file.string, "find", fll_error_file_type_file_e);
+
+ return status;
+ }
+
+ if (status == F_true) {
+ controller_print_error_file(thread, main->error, F_file_found, "f_file_exists", F_true, pid_file.string, "find", fll_error_file_type_file_e);
+
+ return F_status_set_error(F_file_found);
+ }
+
+ status = controller_dynamic_append_terminated(pid_file, child_pid_file);
+
+ if (F_status_is_error(status)) {
+ controller_print_error(thread, main->error, F_status_set_fine(status), "controller_dynamic_append_terminated", F_true);
+
+ return status;
+ }
+
+ if (options & controller_process_option_simulate_d) {
+ if (main->error.verbosity != f_console_verbosity_quiet_e) {
+ controller_lock_print(main->error.to, thread);
+
+ fl_print_format("%cSimulating execution of '%[", main->error.to.stream, f_string_eol_s[0], main->context.set.title);
+
+ if (program) {
+ f_print_safely_terminated(program, main->error.to.stream);
+ }
+ else {
+ f_print_dynamic_safely(arguments.array[0], main->error.to.stream);
+ }
+
+ fl_print_format("%]' with the arguments: '%[", main->error.to.stream, main->context.set.title, main->context.set.important);
+
+ for (f_array_length_t i = program ? 0 : 1; i < arguments.used; ++i) {
+
+ if (program && i || !program && i > 1) {
+ f_print_terminated(f_string_space_s, main->error.to.stream);
+ }
+
+ f_print_dynamic_safely(arguments.array[i], main->error.to.stream);
+ } // for
+
+ fl_print_format("%]' from '", main->error.to.stream, main->context.set.important);
+ fl_print_format("%[%Q%]'.%c", main->error.to.stream, main->context.set.notable, process->rule.name, main->context.set.notable, f_string_eol_s[0]);
+
+ controller_unlock_print_flush(main->error.to, thread);
+ }
+
+ // Sleep for less than a second to better show simulation of synchronous vs asynchronous.
+ {
+ const struct timespec delay = controller_time_milliseconds(controller_thread_simulation_timeout_d);
+
+ if (controller_time_sleep_nanoseconds(main, (controller_setting_t *) process->main_setting, delay) == -1) {
+ status = F_status_set_error(F_interrupt);
+ }
+ }
+
+ if (F_status_set_fine(status) != F_interrupt) {
+ const f_string_static_t simulated_program = macro_f_string_static_t_initialize(f_string_empty_s, 0);
+ const f_string_statics_t simulated_arguments = f_string_statics_t_initialize;
+ fl_execute_parameter_t simulated_parameter = macro_fl_execute_parameter_t_initialize(execute_set->parameter.option, execute_set->parameter.wait, process->rule.has & controller_rule_has_environment_d ? execute_set->parameter.environment : 0, execute_set->parameter.signals, &simulated_program);
+
+ status = fll_execute_program(controller_default_program_script_s, simulated_arguments, &simulated_parameter, &execute_set->as, (void *) &result);
+ }
+ }
+ else {
+ status = fll_execute_program(program, arguments, &execute_set->parameter, &execute_set->as, (void *) &result);
+ }
+
+ if (status == F_parent) {
+ const pid_t id_child = result.pid;
+ result.status = 0;
+
+ f_thread_unlock(&process->lock);
+
+ status_lock = controller_lock_write_process(process, thread, &process->lock);
+
+ if (F_status_is_error(status_lock)) {
+ controller_lock_print_error_critical(main->error, F_status_set_fine(status_lock), F_false, thread);
+
+ if (F_status_set_fine(status_lock) != F_interrupt) {
+ status = controller_lock_read_process(process, thread, &process->lock);
+
+ if (status == F_none) {
+ return status_lock;
+ }
+ }
+
+ return F_status_set_error(F_lock);
+ }
+
+ // Assign the child process id to allow for the cancel process to send appropriate termination signals to the child process.
+ *child = id_child;
+
+ f_thread_unlock(&process->lock);
+
+ status_lock = controller_lock_read_process(process, thread, &process->lock);
+
+ if (F_status_is_error(status_lock)) {
+ controller_lock_print_error_critical(main->error, F_status_set_fine(status_lock), F_true, thread);
+ }
+
+ if (F_status_set_fine(status_lock) != F_interrupt) {
+
+ // The child process should perform the change into background, therefore it is safe to wait for the child to exit (another process is spawned).
+ waitpid(id_child, &result.status, 0);
+ }
+
+ if (!controller_thread_is_enabled_process(process, thread)) {
+ if (status_lock == F_none) {
+ return F_status_set_error(F_interrupt);
+ }
+
+ return F_status_set_error(F_lock);
+ }
+
+ if (status_lock == F_none) {
+ f_thread_unlock(&process->lock);
+ }
+
+ status_lock = controller_lock_write_process(process, thread, &process->lock);
+
+ if (F_status_is_error(status_lock)) {
+ controller_lock_print_error_critical(main->error, F_status_set_fine(status_lock), F_false, thread);
+
+ if (F_status_set_fine(status_lock) != F_interrupt) {
+ status = controller_lock_read_process(process, thread, &process->lock);
+
+ if (status == F_none) {
+ return status_lock;
+ }
+ }
+
+ return F_status_set_error(F_lock);
+ }
+
+ process->result = result.status;
+
+ // Remove the pid now that waidpid() has returned.
+ *child = 0;
+
+ f_thread_unlock(&process->lock);
+
+ status_lock = controller_lock_read_process(process, thread, &process->lock);
+
+ if (F_status_is_error(status_lock)) {
+ controller_lock_print_error_critical(main->error, F_status_set_fine(status_lock), F_true, thread);
+
+ return F_status_set_error(F_lock);
+ }
+
+ if (WIFEXITED(result.status) ? WEXITSTATUS(result.status) : 0) {
+ status = F_status_set_error(F_failure);
+ }
+ else {
+ status = F_none;
+ }
+ }
+ else {
+ main->child = result.status;
+
+ if (!controller_thread_is_enabled_process(process, thread)) {
+ return F_status_set_error(F_interrupt);
+ }
+ }
+
+ if (F_status_is_error(status)) {
+ status = F_status_set_fine(status);
+
+ if (status == F_child || status == F_capability || status == F_group || status == F_nice || status == F_user) {
+ status = F_child;
+ }
+ else {
+ status = F_status_set_error(status);
+ }
+ }
+
+ if (status == F_child || F_status_set_fine(status) == F_interrupt) {
+ return status;
+ }
+
+ if (F_status_is_error(status)) {
+ status = F_status_set_fine(status);
+
+ if ((WIFEXITED(process->result) && WEXITSTATUS(process->result)) || status == F_control_group || status == F_failure || status == F_limit || status == F_processor || status == F_schedule) {
+ controller_rule_item_print_error_execute(type == controller_rule_item_type_utility_e, program ? program : arguments.used ? arguments.array[0].string : f_string_empty_s, status, process);
+ }
+ else {
+ controller_print_error(thread, main->error, F_status_set_fine(status), "fll_execute_program", F_true);
+ }
+
+ return F_status_set_error(status);
+ }
+
+ return status;
+ }
+#endif // _di_controller_rule_execute_pid_with_
+
+#ifndef _di_controller_rule_execute_rerun_
+ int8_t controller_rule_execute_rerun(const uint8_t action, controller_process_t * const process, controller_rule_item_t * const item) {
+
+ const int result = WIFEXITED(process->result) ? WEXITSTATUS(process->result) : 0;
+
+ if (item->reruns[action].is & (result ? controller_rule_rerun_is_failure_d : controller_rule_rerun_is_success_d)) {
+ controller_main_t * const main = (controller_main_t *) process->main_data;
+ controller_thread_t * const thread = (controller_thread_t *) process->main_thread;
+ controller_rule_rerun_item_t *rerun_item = result ? &item->reruns[action].failure : &item->reruns[action].success;
+
+ if (!controller_thread_is_enabled_process(process, thread)) return -2;
+
+ if (!rerun_item->max || rerun_item->count < rerun_item->max) {
+ if (main->error.verbosity == f_console_verbosity_debug_e) {
+ controller_lock_print(main->output.to, thread);
+
+ fl_print_format("%cRe-running '", main->output.to.stream, f_string_eol_s[0]);
+ fl_print_format("%[%q%]", main->output.to.stream, main->context.set.title, process->rule.alias, main->context.set.title);
+ f_print_terminated("' '", main->output.to.stream);
+ fl_print_format("%[%q%]", main->output.to.stream, main->context.set.notable, controller_rule_action_type_execute_name(action), main->context.set.notable);
+ f_print_terminated("' with a ", main->output.to.stream);
+ fl_print_format("%[%s%]", main->output.to.stream, main->context.set.notable, controller_delay_s, main->context.set.notable);
+ f_print_terminated(" of ", main->output.to.stream);
+ fl_print_format("%[%ul%] MegaTime", main->output.to.stream, main->context.set.notable, rerun_item->delay, main->context.set.notable);
+
+ if (rerun_item->max) {
+ f_print_terminated(" for ", main->output.to.stream);
+ fl_print_format("%[%ul%]", main->output.to.stream, main->context.set.notable, rerun_item->count, main->context.set.notable);
+ f_print_terminated(" of ", main->output.to.stream);
+ fl_print_format("%[%s%] ", main->output.to.stream, main->context.set.notable, controller_max_s, main->context.set.notable);
+ fl_print_format("%[%ul%]", main->output.to.stream, main->context.set.notable, rerun_item->max, main->context.set.notable);
+ fl_print_format(".%c", main->output.to.stream, f_string_eol_s[0]);
+ }
+ else {
+ fl_print_format(" with no %[%s%].%c", main->output.to.stream, main->context.set.notable, controller_max_s, main->context.set.notable, f_string_eol_s[0]);
+ }
+
+ controller_unlock_print_flush(main->output.to, thread);
+ }
+
+ if (rerun_item->delay) {
+ const struct timespec delay = controller_time_milliseconds(rerun_item->delay);
+
+ if (controller_time_sleep_nanoseconds(main, (controller_setting_t *) process->main_setting, delay) == -1) {
+ return -1;
+ }
+
+ if (!controller_thread_is_enabled_process(process, thread)) return -2;
+ }
+
+ if (item->reruns[action].is & (result ? controller_rule_rerun_is_failure_reset_d : controller_rule_rerun_is_success_reset_d)) {
+ if (result) {
+ item->reruns[action].success.count = 0;
+ }
+ else {
+ item->reruns[action].failure.count = 0;
+ }
+ }
+
+ if (rerun_item->max) {
+ ++rerun_item->count;
+ }
+
+ return F_true;
+ }
+ }
+
+ return F_false;
+ }
+#endif // _di_controller_rule_execute_rerun_
+
+#ifndef _di_controller_rule_id_construct_
+ f_status_t controller_rule_id_construct(const controller_global_t global, const f_string_static_t source, const f_string_range_t directory, const f_string_range_t basename, f_string_dynamic_t * const alias) {
+
+ f_status_t status = F_none;
+
+ alias->used = 0;
+
+ status = f_string_dynamic_partial_append_nulless(source, directory, alias);
+
+ if (F_status_is_error(status)) {
+ controller_print_error(global.thread, global.main->error, F_status_set_fine(status), "f_string_dynamic_partial_append_nulless", F_true);
+
+ return status;
+ }
+
+ status = f_string_append(f_path_separator_s, F_path_separator_s_length, alias);
+
+ if (F_status_is_error(status)) {
+ controller_print_error(global.thread, global.main->error, F_status_set_fine(status), "f_string_append", F_true);
+
+ return status;
+ }
+
+ status = f_string_dynamic_partial_append_nulless(source, basename, alias);
+
+ if (F_status_is_error(status)) {
+ controller_print_error(global.thread, global.main->error, F_status_set_fine(status), "f_string_dynamic_partial_append_nulless", F_true);
+
+ return status;
+ }
+
+ status = f_string_dynamic_terminate_after(alias);
+
+ if (F_status_is_error(status)) {
+ controller_print_error(global.thread, global.main->error, F_status_set_fine(status), "f_string_dynamic_terminate_after", F_true);
+ }
+
+ return status;
+ }
+#endif // _di_controller_rule_id_construct_
+
+#ifndef _di_controller_rule_status_is_available_
+ f_status_t controller_rule_status_is_available(const uint8_t action, const controller_rule_t rule) {
+
+ return F_status_is_error_not(rule.status[0]) && rule.status[action] == F_known_not;
+ }
+#endif // _di_controller_rule_status_is_available_
+
+#ifndef _di_controller_rule_status_is_error_
+ f_status_t controller_rule_status_is_error(const uint8_t action, const controller_rule_t rule) {
+
+ return F_status_is_error(rule.status[0]) || F_status_is_error(rule.status[action]);
+ }
+#endif // _di_controller_rule_status_is_error_
+
+#ifndef _di_controller_rule_item_read_
+ f_status_t controller_rule_item_read(const controller_global_t global, const bool is_normal, controller_cache_t * const cache, controller_rule_item_t * const item) {
+
+ f_status_t status = F_none;
+ controller_state_interrupt_t custom = macro_controller_state_interrupt_t_initialize(is_normal, global.thread);
+ f_state_t state = macro_f_state_t_initialize(controller_common_allocation_large_d, controller_common_allocation_small_d, 0, &controller_thread_signal_state_fss, 0, (void *) &custom, 0);
+ f_string_range_t range = macro_f_string_range_t_initialize(cache->buffer_item.used);
+ f_array_length_t last = 0;
+
+ uint8_t type = 0;
+ uint8_t method = 0;
+ bool multiple = F_false;
+
+ item->actions.used = 0;
+
+ for (; range.start < cache->buffer_item.used && range.start <= range.stop; last = range.start, cache->delimits.used = 0, cache->comments.used = 0) {
+
+ status = fl_fss_extended_list_object_read(cache->buffer_item, state, &range, &cache->range_action, &cache->delimits);
+
+ if (F_status_is_error(status)) {
+ controller_print_error(global.thread, global.main->error, F_status_set_fine(status), "fl_fss_extended_list_object_read", F_true);
+
+ break;
+ }
+
+ if (status == F_fss_found_object) {
+ multiple = F_true;
+ }
+ else {
+ range.start = last;
+ multiple = F_false;
+ 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, state, &range, &cache->range_action, 0, &cache->delimits);
+
+ if (F_status_is_error(status)) {
+ controller_print_error(global.thread, global.main->error, F_status_set_fine(status), "fl_fss_extended_object_read", F_true);
+
+ break;
+ }
+
+ // Nothing of importance here, so continue onto the next line.
+ // @todo present an error if this line is anything but whitespace.
+ if (status != F_fss_found_object) continue;
+ }
+
+ status = fl_fss_apply_delimit(cache->delimits, &cache->buffer_item);
+
+ if (F_status_is_error(status)) {
+ controller_print_error(global.thread, global.main->error, F_status_set_fine(status), "fl_fss_apply_delimit", F_true);
+
+ break;
+ }
+
+ status = f_fss_count_lines(cache->buffer_item, cache->range_action.start, &cache->action.line_action);
+
+ if (F_status_is_error(status)) {
+ controller_print_error(global.thread, global.main->error, F_status_set_fine(status), "f_fss_count_lines", F_true);
+
+ break;
+ }
+
+ cache->action.line_action += ++item->line;
+ cache->action.name_action.used = 0;
+
+ status = controller_dynamic_rip_nulless_terminated(cache->buffer_item, cache->range_action, &cache->action.name_action);
+
+ if (F_status_is_error(status)) {
+ controller_print_error(global.thread, global.main->error, F_status_set_fine(status), "controller_dynamic_rip_nulless_terminated", F_true);
+
+ break;
+ }
+
+ if (fl_string_dynamic_compare_string(controller_freeze_s, cache->action.name_action, controller_freeze_s_length) == F_equal_to) {
+ type = controller_rule_action_type_freeze_e;
+ }
+ else if (fl_string_dynamic_compare_string(controller_group_s, cache->action.name_action, controller_group_s_length) == F_equal_to) {
+ type = controller_rule_action_type_group_e;
+ }
+ else if (fl_string_dynamic_compare_string(controller_kill_s, cache->action.name_action, controller_kill_s_length) == F_equal_to) {
+ type = controller_rule_action_type_kill_e;
+ }
+ else if (fl_string_dynamic_compare_string(controller_pause_s, cache->action.name_action, controller_pause_s_length) == F_equal_to) {
+ type = controller_rule_action_type_pause_e;
+ }
+ else if (fl_string_dynamic_compare_string(controller_pid_file_s, cache->action.name_action, controller_pid_file_s_length) == F_equal_to) {
+ type = controller_rule_action_type_pid_file_e;
+ }
+ else if (fl_string_dynamic_compare_string(controller_reload_s, cache->action.name_action, controller_reload_s_length) == F_equal_to) {
+ type = controller_rule_action_type_reload_e;
+ }
+ else if (fl_string_dynamic_compare_string(controller_rerun_s, cache->action.name_action, controller_rerun_s_length) == F_equal_to) {
+ type = controller_rule_action_type_rerun_e;
+ }
+ else if (fl_string_dynamic_compare_string(controller_restart_s, cache->action.name_action, controller_restart_s_length) == F_equal_to) {
+ type = controller_rule_action_type_restart_e;
+ }
+ else if (fl_string_dynamic_compare_string(controller_resume_s, cache->action.name_action, controller_resume_s_length) == F_equal_to) {
+ type = controller_rule_action_type_resume_e;
+ }
+ else if (fl_string_dynamic_compare_string(controller_start_s, cache->action.name_action, controller_start_s_length) == F_equal_to) {
+ type = controller_rule_action_type_start_e;
+ }
+ else if (fl_string_dynamic_compare_string(controller_stop_s, cache->action.name_action, controller_stop_s_length) == F_equal_to) {
+ type = controller_rule_action_type_stop_e;
+ }
+ else if (fl_string_dynamic_compare_string(controller_thaw_s, cache->action.name_action, controller_thaw_s_length) == F_equal_to) {
+ type = controller_rule_action_type_thaw_e;
+ }
+ else if (fl_string_dynamic_compare_string(controller_user_s, cache->action.name_action, controller_user_s_length) == F_equal_to) {
+ type = controller_rule_action_type_user_e;
+ }
+ else if (fl_string_dynamic_compare_string(controller_with_s, cache->action.name_action, controller_with_s_length) == F_equal_to) {
+ type = controller_rule_action_type_with_e;
+ }
+ else {
+ if (global.main->warning.verbosity == f_console_verbosity_debug_e) {
+ controller_lock_print(global.main->warning.to, global.thread);
+
+ fl_print_format("%c%[%SUnknown rule item action '%]", global.main->warning.to.stream, f_string_eol_s[0], global.main->warning.context, global.main->warning.prefix, global.main->warning.context);
+ fl_print_format("%[%Q%]", global.main->warning.to.stream, global.main->warning.notable, cache->action.name_action, global.main->warning.notable);
+ fl_print_format("%['.%]%c", global.main->warning.to.stream, global.main->warning.context, global.main->warning.context, f_string_eol_s[0]);
+
+ controller_rule_print_error_cache(global.main->warning, cache->action, F_true);
+
+ controller_unlock_print_flush(global.main->warning.to, global.thread);
+ }
+
+ continue;
+ }
+
+ if (multiple) {
+ if (type == controller_rule_action_type_group_e || type == controller_rule_action_type_pid_file_e || type == controller_rule_action_type_user_e) {
+
+ if (global.main->error.verbosity != f_console_verbosity_quiet_e) {
+ controller_lock_print(global.main->error.to, global.thread);
+
+ fl_print_format("%c%[%SFSS Extended List is not allowed for the rule item action '%]", global.main->error.to.stream, f_string_eol_s[0], global.main->error.context, global.main->error.prefix, global.main->error.context);
+ fl_print_format("%[%Q%]", global.main->error.to.stream, global.main->error.notable, cache->action.name_action, global.main->error.notable);
+ fl_print_format("%['.%]%c", global.main->error.to.stream, global.main->error.context, global.main->error.context, f_string_eol_s[0]);
+
+ controller_unlock_print_flush(global.main->error.to, global.thread);
+ }
+
+ status = F_status_set_error(F_supported_not);
+ break;
+ }
+
+ method = controller_rule_action_method_extended_list_e;
+ }
+ else {
+ method = controller_rule_action_method_extended_e;
+ }
+
+ status = controller_rule_actions_increase_by(controller_common_allocation_small_d, &item->actions);
+
+ if (F_status_is_error(status)) {
+ controller_print_error(global.thread, global.main->error, F_status_set_fine(status), "controller_rule_actions_increase_by", F_true);
+
+ break;
+ }
+
+ status = controller_rule_action_read(global, is_normal, type, method, cache, item, &item->actions, &range);
+ if (F_status_is_error(status)) break;
+ } // for
+
+ return status;
+ }
+#endif // _di_controller_rule_item_read_
+
+#ifndef _di_controller_rule_item_type_name_
+ f_string_static_t controller_rule_item_type_name(const uint8_t type) {
+
+ f_string_static_t buffer = f_string_static_t_initialize;
+
+ switch (type) {
+ case controller_rule_item_type_command_e:
+ buffer.string = controller_command_s;
+ buffer.used = controller_command_s_length;
+ break;
+
+ case controller_rule_item_type_script_e:
+ buffer.string = controller_script_s;
+ buffer.used = controller_script_s_length;
+ break;
+
+ case controller_rule_item_type_service_e:
+ buffer.string = controller_service_s;
+ buffer.used = controller_service_s_length;
+ break;
+
+ case controller_rule_item_type_setting_e:
+ buffer.string = controller_setting_s;
+ buffer.used = controller_setting_s_length;
+ break;
+
+ case controller_rule_item_type_utility_e:
+ buffer.string = controller_utility_s;
+ buffer.used = controller_utility_s_length;
+ break;
+ }
+
+ buffer.size = buffer.used;
+
+ return buffer;
+ }
+#endif // _di_controller_rule_item_type_name_
+
+#ifndef _di_controller_rule_items_increase_by_
+ f_status_t controller_rule_items_increase_by(const f_array_length_t amount, controller_rule_items_t * const items) {
+
+ if (items->used + amount > items->size) {
+ if (items->used + amount > F_array_length_t_size_d) {
+ return F_status_set_error(F_array_too_large);
+ }
+
+ const f_status_t status = f_memory_resize(items->size, items->used + amount, sizeof(controller_rule_item_t), (void **) & items->array);
+
+ if (F_status_is_error_not(status)) {
+ items->size = items->used + amount;
+ }
+
+ return status;
+ }
+
+ return F_data_not;
+ }
+#endif // _di_controller_rule_items_increase_by_
+
+#ifndef _di_controller_rule_setting_limit_type_name_
+ f_string_static_t controller_rule_setting_limit_type_name(const uint8_t type) {
+
+ f_string_static_t buffer = f_string_static_t_initialize;
+
+ switch (type) {
+ case controller_resource_limit_type_as_e:
+ buffer.string = controller_as_s;
+ buffer.used = controller_as_s_length;
+ break;
+
+ case controller_resource_limit_type_core_e:
+ buffer.string = controller_core_s;
+ buffer.used = controller_core_s_length;
+ break;
+
+ case controller_resource_limit_type_cpu_e:
+ buffer.string = controller_cpu_s;
+ buffer.used = controller_cpu_s_length;
+ break;
+
+ case controller_resource_limit_type_data_e:
+ buffer.string = controller_data_s;
+ buffer.used = controller_data_s_length;
+ break;
+
+ case controller_resource_limit_type_fsize_e:
+ buffer.string = controller_fsize_s;
+ buffer.used = controller_fsize_s_length;
+ break;
+
+ case controller_resource_limit_type_locks_e:
+ buffer.string = controller_locks_s;
+ buffer.used = controller_locks_s_length;
+ break;
+
+ case controller_resource_limit_type_memlock_e:
+ buffer.string = controller_memlock_s;
+ buffer.used = controller_memlock_s_length;
+ break;
+
+ case controller_resource_limit_type_msgqueue_e:
+ buffer.string = controller_msgqueue_s;
+ buffer.used = controller_msgqueue_s_length;
+ break;
+
+ case controller_resource_limit_type_nice_e:
+ buffer.string = controller_nice_s;
+ buffer.used = controller_nice_s_length;
+ break;
+
+ case controller_resource_limit_type_nofile_e:
+ buffer.string = controller_nofile_s;
+ buffer.used = controller_nofile_s_length;
+ break;
+
+ case controller_resource_limit_type_nproc_e:
+ buffer.string = controller_nproc_s;
+ buffer.used = controller_nproc_s_length;
+ break;
+
+ case controller_resource_limit_type_rss_e:
+ buffer.string = controller_rss_s;
+ buffer.used = controller_rss_s_length;
+ break;
+
+ case controller_resource_limit_type_rtprio_e:
+ buffer.string = controller_rtprio_s;
+ buffer.used = controller_rtprio_s_length;
+ break;
+
+ case controller_resource_limit_type_rttime_e:
+ buffer.string = controller_rttime_s;
+ buffer.used = controller_rttime_s_length;
+ break;
+
+ case controller_resource_limit_type_sigpending_e:
+ buffer.string = controller_sigpending_s;
+ buffer.used = controller_sigpending_s_length;
+ break;
+
+ case controller_resource_limit_type_stack_e:
+ buffer.string = controller_stack_s;
+ buffer.used = controller_stack_s_length;
+ break;
+ }
+
+ buffer.size = buffer.used;
+
+ return buffer;
+ }
+#endif // _di_controller_rule_setting_limit_type_name_
+
+#ifndef _di_controller_rule_process_
+ f_status_t controller_rule_process(const controller_global_t global, controller_process_t * const process) {
+
+ switch (process->action) {
+ case controller_rule_action_type_freeze_e:
+ case controller_rule_action_type_kill_e:
+ case controller_rule_action_type_pause_e:
+ case controller_rule_action_type_reload_e:
+ case controller_rule_action_type_restart_e:
+ case controller_rule_action_type_resume_e:
+ case controller_rule_action_type_start_e:
+ case controller_rule_action_type_stop_e:
+ case controller_rule_action_type_thaw_e:
+ break;
+
+ default:
+ if (global.main->error.verbosity != f_console_verbosity_quiet_e) {
+ controller_lock_print(global.main->error.to, global.thread);
+
+ fl_print_format("%c%[%SUnsupported action type '%]", global.main->error.to.stream, f_string_eol_s[0], global.main->error.context, global.main->error.prefix, global.main->error.context);
+ fl_print_format("%[%q%]", global.main->error.to.stream, global.main->error.notable, controller_rule_action_type_name(process->action), global.main->error.notable);
+ fl_print_format("%[' while attempting to execute rule.%]%c", global.main->error.to.stream, global.main->error.context, global.main->error.context, f_string_eol_s[0]);
+
+ controller_rule_print_error_cache(global.main->error, process->cache.action, F_true);
+
+ controller_unlock_print_flush(global.main->error.to, global.thread);
+ }
+
+ return F_status_set_error(F_parameter);
+ }
+
+ f_status_t status = F_none;
+ f_status_t status_lock = F_none;
+
+ process->cache.action.name_action.used = 0;
+ process->cache.action.name_item.used = 0;
+ process->cache.action.name_file.used = 0;
+
+ status = f_string_append(controller_rules_s, controller_rules_s_length, &process->cache.action.name_file);
+
+ if (F_status_is_error_not(status)) {
+ status = f_string_append(f_path_separator_s, F_path_separator_s_length, &process->cache.action.name_file);
+ }
+
+ if (F_status_is_error(status)) {
+ controller_rule_print_error(global.thread, global.main->error, process->cache.action, F_status_set_fine(status), "f_string_append", F_true, F_true);
+
+ return status;
+ }
+
+ status = f_string_dynamic_append(process->rule.alias, &process->cache.action.name_file);
+
+ if (F_status_is_error(status)) {
+ controller_rule_print_error(global.thread, global.main->error, process->cache.action, F_status_set_fine(status), "f_string_dynamic_append", F_true, F_true);
+
+ return status;
+ }
+
+ status = f_string_append(F_path_extension_separator_s, F_path_extension_separator_s_length, &process->cache.action.name_file);
+
+ if (F_status_is_error_not(status)) {
+ status = f_string_append(controller_rule_s, controller_rule_s_length, &process->cache.action.name_file);
+ }
+
+ if (F_status_is_error(status)) {
+ controller_rule_print_error(global.thread, global.main->error, process->cache.action, F_status_set_fine(status), "f_string_append", F_true, F_true);
+
+ return status;
+ }
+
+ status = f_string_dynamic_terminate_after(&process->cache.action.name_file);
+
+ if (F_status_is_error(status)) {
+ controller_rule_print_error(global.thread, global.main->error, process->cache.action, F_status_set_fine(status), "f_string_dynamic_terminate_after", F_true, F_true);
+
+ return status;
+ }
+
+ if ((process->options & controller_process_option_simulate_d) && (process->options & controller_process_option_validate_d)) {
+ controller_rule_validate(global, process->rule, process->action, process->options, &process->cache);
+ }
+
+ f_array_length_t i = 0;
+
+ {
+ f_array_length_t j = 0;
+ f_array_length_t k = 0;
+ f_array_length_t id_rule = 0;
+ f_array_length_t id_dependency = 0;
+
+ bool found = F_false;
+
+ controller_process_t *dependency = 0;
+
+ uint8_t options_process = 0;
+
+ const f_string_t strings[3] = {
+ "needed",
+ "wanted",
+ "wished for",
+ };
+
+ f_string_dynamics_t empty = f_string_dynamics_t_initialize;
+ f_string_dynamics_t *dynamics[3] = { &empty, &empty, &empty };
+
+ if (process->action) {
+
+ for (i = 0; i < process->rule.ons.used; ++i) {
+
+ if (process->rule.ons.array[i].action == process->action) {
+ dynamics[0] = &process->rule.ons.array[i].need;
+ dynamics[1] = &process->rule.ons.array[i].want;
+ dynamics[2] = &process->rule.ons.array[i].wish;
+
+ break;
+ }
+ } // for
+ }
+
+ // i==0 is need, i==1 is want, i==2 is wish.
+ // loop through all dependencies: wait for depedency, execute dependency, fail due to missing required dependency, or skip unrequired missing dependencies.
+ for (i = 0; i < 3 && controller_thread_is_enabled_process(process, global.thread); ++i) {
+
+ for (j = 0; j < dynamics[i]->used && controller_thread_is_enabled_process(process, global.thread); ++j) {
+
+ id_dependency = 0;
+ dependency = 0;
+ found = F_false;
+
+ status_lock = controller_lock_read_process(process, global.thread, &global.thread->lock.process);
+
+ if (F_status_is_error(status_lock)) {
+ controller_lock_print_error_critical(global.main->error, F_status_set_fine(status_lock), F_true, global.thread);
+ }
+ else {
+ status = controller_process_prepare_process_type(global, process->type, process->action, dynamics[i]->array[j], &id_dependency);
+
+ if (F_status_is_error(status)) {
+ if (F_status_set_fine(status) == F_lock) {
+ if (!controller_thread_is_enabled_process_type(process->type, global.thread)) {
+ return F_status_set_error(F_interrupt);
+ }
+ }
+
+ if (global.main->error.verbosity != f_console_verbosity_quiet_e) {
+ controller_lock_print(global.main->error.to, global.thread);
+
+ controller_rule_item_print_error_rule_not_loaded(global.main->error, dynamics[i]->array[j].string);
+ controller_rule_print_error_cache(global.main->error, process->cache.action, F_false);
+
+ controller_unlock_print_flush(global.main->error.to, global.thread);
+ }
+
+ return status;
+ }
+
+ status = F_true;
+ }
+
+ if (status == F_true) {
+ found = F_true;
+
+ dependency = global.thread->processs.array[id_dependency];
+
+ status_lock = controller_lock_read_process(process, global.thread, &dependency->active);
+
+ if (F_status_is_error(status_lock)) {
+ controller_lock_print_error_critical(global.main->error, F_status_set_fine(status_lock), F_true, global.thread);
+
+ status = F_false;
+ dependency = 0;
+
+ f_thread_unlock(&global.thread->lock.process);
+ }
+ else {
+ f_thread_unlock(&global.thread->lock.process);
+
+ status_lock = controller_lock_read_process(process, global.thread, &global.thread->lock.rule);
+
+ if (F_status_is_error(status_lock)) {
+ controller_lock_print_error_critical(global.main->error, F_status_set_fine(status_lock), F_true, global.thread);
+
+ status = F_false;
+ }
+ else {
+ status = controller_rule_find(dynamics[i]->array[j], global.setting->rules, &id_rule);
+
+ f_thread_unlock(&global.thread->lock.rule);
+ }
+ }
+ }
+ else {
+ f_thread_unlock(&global.thread->lock.process);
+ }
+
+ if (status != F_true) {
+ found = F_false;
+ id_rule = 0;
+
+ if (i == 0) {
+ controller_lock_print(global.main->error.to, global.thread);
+
+ controller_rule_item_print_error_need_want_wish(global.main->error, strings[i], dynamics[i]->array[j].string, "was not found");
+ controller_rule_print_error_cache(global.main->error, process->cache.action, F_true);
+
+ controller_unlock_print_flush(global.main->error.to, global.thread);
+
+ status = F_status_set_error(F_found_not);
+
+ if (!(process->options & controller_process_option_simulate_d)) {
+ if (dependency) {
+ f_thread_unlock(&dependency->active);
+ }
+
+ break;
+ }
+ }
+ else {
+ if (global.main->warning.verbosity == f_console_verbosity_debug_e) {
+ controller_lock_print(global.main->warning.to, global.thread);
+
+ controller_rule_item_print_error_need_want_wish(global.main->warning, strings[i], dynamics[i]->array[j].string, "was not found");
+
+ controller_rule_print_error_cache(global.main->warning, process->cache.action, F_true);
+
+ controller_unlock_print_flush(global.main->warning.to, global.thread);
+ }
+ }
+ }
+ else if (found) {
+ status_lock = controller_lock_read_process(process, global.thread, &global.thread->lock.rule);
+
+ if (F_status_is_error(status_lock)) {
+ controller_lock_print_error_critical(global.main->error, F_status_set_fine(status_lock), F_true, global.thread);
+
+ found = F_false;
+ status = status_lock;
+ }
+ }
+
+ if (found) {
+
+ // the dependency may have write locks, which needs to be avoided, so copy the alias from the rule.
+ char alias_other_buffer[global.setting->rules.array[id_rule].alias.used + 1];
+
+ memcpy(alias_other_buffer, global.setting->rules.array[id_rule].alias.string, global.setting->rules.array[id_rule].alias.used);
+ alias_other_buffer[global.setting->rules.array[id_rule].alias.used] = 0;
+
+ const f_string_static_t alias_other = macro_f_string_static_t_initialize(alias_other_buffer, global.setting->rules.array[id_rule].alias.used);
+
+ f_thread_unlock(&global.thread->lock.rule);
+
+ status_lock = controller_lock_read_process(process, global.thread, &dependency->lock);
+
+ if (F_status_is_error(status_lock)) {
+ controller_lock_print_error_critical(global.main->error, F_status_set_fine(status_lock), F_true, global.thread);
+
+ status = status_lock;
+ }
+ else if (dependency->state == controller_process_state_active_e || dependency->state == controller_process_state_busy_e) {
+ f_thread_unlock(&dependency->lock);
+
+ status = controller_process_wait(global, dependency);
+
+ if (F_status_is_error(status) && !(process->options & controller_process_option_simulate_d)) break;
+
+ status = dependency->rule.status[process->action];
+ }
+ else {
+ status_lock = controller_lock_read_process(process, global.thread, &global.thread->lock.rule);
+
+ if (F_status_is_error(status_lock)) {
+ controller_lock_print_error_critical(global.main->error, F_status_set_fine(status_lock), F_true, global.thread);
+
+ f_thread_unlock(&dependency->lock);
+
+ status = status_lock;
+ }
+ else if (controller_rule_status_is_available(process->action, global.setting->rules.array[id_rule])) {
+ f_thread_unlock(&global.thread->lock.rule);
+ f_thread_unlock(&dependency->lock);
+
+ options_process = 0;
+
+ if (global.main->parameters[controller_parameter_simulate_e].result == f_console_result_found_e) {
+ options_process |= controller_process_option_simulate_d;
+ }
+
+ if (process->options & controller_process_option_validate_d) {
+ options_process |= controller_process_option_validate_d;
+ }
+
+ // Synchronously execute dependency.
+ status = controller_rule_process_begin(global, 0, alias_other, process->action, options_process, process->type, process->stack, dependency->cache);
+
+ if (status == F_child || F_status_set_fine(status) == F_interrupt) {
+ f_thread_unlock(&dependency->active);
+
+ break;
+ }
+
+ if (F_status_is_error(status)) {
+ if (i == 0 || i == 1 || F_status_set_fine(status) == F_memory_not) {
+ controller_lock_print(global.main->error.to, global.thread);
+
+ controller_rule_item_print_error_need_want_wish(global.main->error, strings[i], alias_other_buffer, "failed during execution");
+ controller_rule_print_error_cache(global.main->error, process->cache.action, F_true);
+
+ controller_unlock_print_flush(global.main->error.to, global.thread);
+
+ if (!(dependency->options & controller_process_option_simulate_d) || F_status_set_fine(status) == F_memory_not) {
+ f_thread_unlock(&dependency->active);
+
+ break;
+ }
+ }
+ else {
+ if (global.main->warning.verbosity == f_console_verbosity_debug_e) {
+ controller_lock_print(global.main->warning.to, global.thread);
+
+ controller_rule_item_print_error_need_want_wish(global.main->warning, strings[i], alias_other_buffer, "failed during execution");
+
+ controller_rule_print_error_cache(global.main->warning, process->cache.action, F_true);
+
+ controller_unlock_print_flush(global.main->warning.to, global.thread);
+ }
+ }
+ }
+ }
+ else {
+ status = global.setting->rules.array[id_rule].status[process->action];
+
+ f_thread_unlock(&global.thread->lock.rule);
+ f_thread_unlock(&dependency->lock);
+ }
+ }
+
+ if (!controller_thread_is_enabled_process(process, global.thread)) {
+ f_thread_unlock(&dependency->active);
+
+ break;
+ }
+
+ if (F_status_is_error_not(status_lock)) {
+ status_lock = controller_lock_read_process(process, global.thread, &global.thread->lock.rule);
+
+ if (F_status_is_error(status_lock)) {
+ controller_lock_print_error_critical(global.main->error, F_status_set_fine(status_lock), F_false, global.thread);
+ }
+ }
+
+ if (F_status_is_error(status_lock)) {
+ if (F_status_is_error(status_lock)) {
+ controller_rule_item_print_error_need_want_wish(global.main->error, strings[i], alias_other_buffer, "due to lock failure");
+ }
+
+ status = status_lock;
+ }
+ else if (controller_rule_status_is_error(process->action, global.setting->rules.array[id_rule])) {
+ f_thread_unlock(&global.thread->lock.rule);
+
+ if (i == 0 || i == 1) {
+ controller_lock_print(global.main->error.to, global.thread);
+
+ controller_rule_item_print_error_need_want_wish(global.main->error, strings[i], alias_other_buffer, "is in a failed state");
+
+ controller_rule_print_error_cache(global.main->error, process->cache.action, F_true);
+
+ controller_unlock_print_flush(global.main->error.to, global.thread);
+
+ status = F_status_set_error(F_found_not);
+
+ if (!(dependency->options & controller_process_option_simulate_d)) {
+ f_thread_unlock(&dependency->active);
+
+ break;
+ }
+ }
+ else {
+ if (global.main->warning.verbosity == f_console_verbosity_debug_e) {
+ controller_lock_print(global.main->warning.to, global.thread);
+
+ controller_rule_item_print_error_need_want_wish(global.main->warning, strings[i], alias_other_buffer, "is in a failed state");
+
+ controller_rule_print_error_cache(global.main->warning, process->cache.action, F_true);
+
+ controller_unlock_print_flush(global.main->warning.to, global.thread);
+ }
+ }
+ }
+ else {
+ f_thread_unlock(&global.thread->lock.rule);
+ }
+ }
+
+ if (dependency) {
+ f_thread_unlock(&dependency->active);
+ }
+ } // for
+
+ if (status == F_child || F_status_set_fine(status) == F_interrupt) break;
+
+ if (F_status_is_error(status) && !(process->options & controller_process_option_simulate_d)) break;
+ } // for
+ }
+
+ if (status == F_child || F_status_set_fine(status) == F_interrupt) {
+ return status;
+ }
+
+ if (!controller_thread_is_enabled_process(process, global.thread)) {
+ return F_status_set_error(F_interrupt);
+ }
+
+ if ((process->options & controller_process_option_wait_d) && F_status_is_error_not(status) && (process->options & controller_process_option_validate_d)) {
+ status_lock = controller_rule_wait_all_process_type(global, process->type, F_false, process);
+
+ if (F_status_set_fine(status_lock) == F_interrupt) {
+ return status_lock;
+ }
+ }
+
+ if (!(process->options & controller_process_option_validate_d) && F_status_is_error_not(status)) {
+
+ // find at least one of the requested action when the rule is required.
+ if (process->options & controller_process_option_require_d) {
+ bool missing = F_true;
+
+ f_array_length_t j = 0;
+
+ for (i = 0; i < process->rule.items.used; ++i) {
+
+ for (j = 0; j < process->rule.items.array[i].actions.used; ++j) {
+
+ if (process->rule.items.array[i].actions.array[j].type == process->action) {
+ missing = F_false;
+ break;
+ }
+ } // for
+ } // for
+
+ if (missing) {
+ if (global.main->error.verbosity != f_console_verbosity_quiet_e) {
+ controller_lock_print(global.main->error.to, global.thread);
+
+ if (process->rule.items.used) {
+ fl_print_format("%c%[%SThe rule '%]", global.main->error.to.stream, f_string_eol_s[0], global.main->error.context, global.main->error.prefix, global.main->error.context);
+ fl_print_format("%[%Q%]", global.main->error.to.stream, global.main->error.notable, process->rule.name, global.main->error.notable);
+ fl_print_format("%[' has no '%]", global.main->error.to.stream, global.main->error.context, global.main->error.context);
+ fl_print_format("%[%q%]", global.main->error.to.stream, global.main->error.notable, controller_rule_action_type_name(process->action), global.main->error.notable);
+ fl_print_format("%[' action to execute.%]%c", global.main->error.to.stream, global.main->error.context, global.main->error.context, f_string_eol_s[0]);
+ }
+ else {
+ fl_print_format("%c%[%SThe rule '%]", global.main->error.to.stream, f_string_eol_s[0], global.main->error.context, global.main->error.prefix, global.main->error.context);
+ fl_print_format("%[%Q%]", global.main->error.to.stream, global.main->error.notable, process->rule.name, global.main->error.notable);
+ fl_print_format("%[ has no known '%]", global.main->error.to.stream, global.main->error.context, global.main->error.context);
+ fl_print_format("%[%s %s%]", global.main->error.to.stream, global.main->error.notable, controller_rule_s, controller_type_s, global.main->error.notable);
+ fl_print_format("%[' (such as '%]", global.main->error.to.stream, global.main->error.context, global.main->error.context);
+ fl_print_format("%[%s%]", global.main->error.to.stream, global.main->error.notable, controller_command_s, global.main->error.notable);
+ fl_print_format("%[', '%]", global.main->error.to.stream, global.main->error.context, global.main->error.context);
+ fl_print_format("%[%s%]", global.main->error.to.stream, global.main->error.notable, controller_service_s, global.main->error.notable);
+ fl_print_format("%[', '%]", global.main->error.to.stream, global.main->error.context, global.main->error.context);
+ fl_print_format("%[%s%]", global.main->error.to.stream, global.main->error.notable, controller_script_s, global.main->error.notable);
+ fl_print_format("%[', or '%]", global.main->error.to.stream, global.main->error.context, global.main->error.context);
+ fl_print_format("%[%s%]", global.main->error.to.stream, global.main->error.notable, controller_utility_s, global.main->error.notable);
+ fl_print_format("%[') to execute.%]%c", global.main->error.to.stream, global.main->error.context, global.main->error.context, f_string_eol_s[0]);
+ }
+
+ controller_rule_print_error_cache(global.main->error, process->cache.action, F_true);
+
+ controller_unlock_print_flush(global.main->error.to, global.thread);
+ }
+
+ status = F_status_set_error(F_parameter);
+ }
+ }
+
+ if (F_status_is_error_not(status)) {
+ status = controller_rule_execute(global, process->action, process->options, process);
+
+ if (status == F_child || F_status_set_fine(status) == F_interrupt || F_status_set_fine(status) == F_lock) {
+ return status;
+ }
+
+ if (F_status_is_error(status)) {
+ controller_rule_item_print_error(global.thread, global.main->error, process->cache.action, F_true, F_status_set_fine(status));
+ }
+ }
+ }
+
+ f_array_length_t id_rule = 0;
+
+ f_thread_unlock(&process->lock);
+
+ status_lock = controller_lock_write_process(process, global.thread, &process->lock);
+
+ if (F_status_is_error(status_lock)) {
+ controller_lock_print_error_critical(global.main->error, F_status_set_fine(status_lock), F_false, global.thread);
+
+ if (F_status_set_fine(status) != F_interrupt) {
+ status = controller_lock_read_process(process, global.thread, &process->lock);
+
+ if (F_status_is_error_not(status)) {
+ return status_lock;
+ }
+ }
+
+ return F_status_set_error(F_lock);
+ }
+
+ if (F_status_is_error(status)) {
+ process->rule.status[process->action] = controller_status_simplify_error(F_status_set_fine(status));
+ }
+ else {
+ process->rule.status[process->action] = status;
+ }
+
+ status_lock = controller_lock_write_process(process, global.thread, &global.thread->lock.rule);
+
+ if (F_status_is_error(status_lock)) {
+ controller_lock_print_error_critical(global.main->error, F_status_set_fine(status_lock), F_false, global.thread);
+
+ f_thread_unlock(&process->lock);
+
+ status = controller_lock_read_process(process, global.thread, &process->lock);
+
+ if (F_status_is_error_not(status)) {
+ return status_lock;
+ }
+
+ return F_status_set_error(F_lock);
+ }
+
+ // Update the global rule status, which is stored separately from the rule status for this process.
+ if (controller_rule_find(process->rule.alias, global.setting->rules, &id_rule) == F_true) {
+ controller_rule_t *rule = &global.setting->rules.array[id_rule];
+
+ rule->status[process->action] = process->rule.status[process->action];
+
+ f_array_length_t j = 0;
+
+ controller_rule_item_t *rule_item = 0;
+ controller_rule_action_t *rule_action = 0;
+
+ // @todo implement a "version" counter and detect if the rule version is different before attempting update.
+ // Copy all rule item action statuses from the rule process to the rule.
+ for (i = 0; i < rule->items.used; ++i) {
+
+ rule_item = &rule->items.array[i];
+
+ for (j = 0; j < rule_item->actions.used; ++j) {
+ rule_item->actions.array[j].status = process->rule.items.array[i].actions.array[j].status;
+ } // for
+ } // for
+ }
+
+ f_thread_unlock(&global.thread->lock.rule);
+ f_thread_unlock(&process->lock);
+
+ status_lock = controller_lock_read_process(process, global.thread, &process->lock);
+
+ if (F_status_is_error(status_lock)) {
+ controller_lock_print_error_critical(global.main->error, F_status_set_fine(status_lock), F_true, global.thread);
+
+ return F_status_set_error(F_lock);
+ }
+
+ return process->rule.status[process->action];
+ }
+#endif // _di_controller_rule_process_
+
+#ifndef _di_controller_rule_process_begin_
+ f_status_t controller_rule_process_begin(const controller_global_t global, const uint8_t options_force, const f_string_static_t alias_rule, const uint8_t action, const uint8_t options, const uint8_t type, const f_array_lengths_t stack, const controller_cache_t cache) {
+
+ if (!controller_thread_is_enabled_process_type(type, global.thread)) {
+ return F_status_set_error(F_interrupt);
+ }
+
+ f_status_t status = F_none;
+ f_status_t status_lock = F_none;
+
+ controller_process_t *process = 0;
+
+ status = controller_lock_read_process_type(type, global.thread, &global.thread->lock.process);
+
+ if (F_status_is_error(status)) {
+ controller_lock_print_error_critical(global.main->error, F_status_set_fine(status), F_true, global.thread);
+
+ return status;
+ }
+
+ {
+ f_array_length_t at = 0;
+
+ status = controller_process_prepare(global, type != controller_process_type_exit_e, action, alias_rule, &at);
+
+ if (F_status_is_error(status)) {
+ f_thread_unlock(&global.thread->lock.process);
+
+ if (global.main->error.verbosity != f_console_verbosity_quiet_e) {
+ controller_lock_print(global.main->error.to, global.thread);
+
+ controller_rule_item_print_error_rule_not_loaded(global.main->error, alias_rule.string);
+ controller_rule_print_error_cache(global.main->error, cache.action, F_false);
+
+ controller_unlock_print_flush(global.main->error.to, global.thread);
+ }
+
+ return status;
+ }
+
+ process = global.thread->processs.array[at];
+
+ status = controller_lock_read_process_type(type, global.thread, &process->active);
+
+ if (F_status_is_error(status)) {
+ controller_lock_print_error_critical(global.main->error, F_status_set_fine(status), F_true, global.thread);
+ controller_rule_item_print_error(global.thread, global.main->error, cache.action, F_false, F_status_set_fine(status));
+
+ f_thread_unlock(&global.thread->lock.process);
+
+ return status;
+ }
+
+ status_lock = controller_lock_write_process(process, global.thread, &process->lock);
+
+ if (F_status_is_error(status_lock)) {
+ controller_lock_print_error_critical(global.main->error, F_status_set_fine(status_lock), F_false, global.thread);
+
+ f_thread_unlock(&process->active);
+ f_thread_unlock(&global.thread->lock.process);
+
+ return status_lock;
+ }
+
+ // once a write lock on the process is achieved, it is safe to unlock the global process read lock.
+ f_thread_unlock(&global.thread->lock.process);
+
+ // if the process is already running, then there is nothing to do.
+ if (process->state == controller_process_state_active_e || process->state == controller_process_state_busy_e) {
+ f_thread_unlock(&process->lock);
+ f_thread_unlock(&process->active);
+
+ return F_busy;
+ }
+
+ // the thread is done, so close the thread.
+ if (process->state == controller_process_state_done_e) {
+ controller_thread_join(&process->id_thread);
+
+ f_thread_mutex_lock(&process->wait_lock);
+ f_thread_condition_signal_all(&process->wait);
+ f_thread_mutex_unlock(&process->wait_lock);
+ }
+
+ process->id = at;
+ }
+
+ f_thread_unlock(&process->lock);
+
+ status_lock = controller_lock_write_process(process, global.thread, &process->lock);
+
+ if (F_status_is_error(status_lock)) {
+ controller_lock_print_error_critical(global.main->error, F_status_set_fine(status_lock), F_false, global.thread);
+
+ f_thread_unlock(&process->active);
+
+ return status_lock;
+ }
+
+ process->state = controller_process_state_active_e;
+ process->action = action;
+ process->options = options;
+ process->type = type;
+
+ macro_f_time_spec_t_clear(process->cache.timestamp)
+ macro_f_string_range_t_clear(process->cache.range_action)
+
+ process->cache.ats.used = 0;
+ process->cache.stack.used = 0;
+ process->cache.comments.used = 0;
+ process->cache.delimits.used = 0;
+ process->cache.content_action.used = 0;
+ process->cache.content_actions.used = 0;
+ process->cache.content_items.used = 0;
+ process->cache.object_actions.used = 0;
+ process->cache.object_items.used = 0;
+ process->cache.buffer_file.used = 0;
+ process->cache.buffer_item.used = 0;
+ process->cache.buffer_path.used = 0;
+ process->cache.action.line_action = cache.action.line_action;
+ process->cache.action.line_item = cache.action.line_item;
+ process->cache.action.name_action.used = 0;
+ process->cache.action.name_file.used = 0;
+ process->cache.action.name_item.used = 0;
+ process->cache.action.generic.used = 0;
+
+ process->stack.used = 0;
+
+ process->main_data = (void *) global.main;
+ process->main_setting = (void *) global.setting;
+ process->main_thread = (void *) global.thread;
+
+ if (F_status_is_error_not(status) && stack.used) {
+ if (process->stack.size < stack.used) {
+ status = f_type_array_lengths_resize(stack.used, &process->stack);
+ }
+
+ if (F_status_is_error(status)) {
+ controller_print_error(global.thread, global.main->error, F_status_set_fine(status), "f_type_array_lengths_resize", F_true);
+ }
+ else {
+ for (f_array_length_t i = 0; i < stack.used; ++i) {
+ process->stack.array[i] = stack.array[i];
+ } // for
+
+ process->stack.used = stack.used;
+ }
+ }
+
+ if (F_status_is_error_not(status)) {
+ status = f_string_dynamic_append(cache.action.name_action, &process->cache.action.name_action);
+
+ if (F_status_is_error_not(status)) {
+ status = f_string_dynamic_append(cache.action.name_file, &process->cache.action.name_file);
+ }
+
+ if (F_status_is_error_not(status)) {
+ status = f_string_dynamic_append(cache.action.name_item, &process->cache.action.name_item);
+ }
+ else {
+ controller_print_error(global.thread, global.main->error, F_status_set_fine(status), "f_string_dynamic_append", F_true);
+ }
+ }
+
+ f_thread_unlock(&process->lock);
+
+ if (F_status_is_error_not(status)) {
+ if (process->action && (options_force & controller_process_option_asynchronous_d)) {
+ if (process->type == controller_process_type_exit_e) {
+ status = f_thread_create(0, &process->id_thread, controller_thread_process_other, (void *) process);
+ }
+ else {
+ status = f_thread_create(0, &process->id_thread, controller_thread_process_normal, (void *) process);
+ }
+
+ if (F_status_is_error(status)) {
+ controller_print_error(global.thread, global.main->error, F_status_set_fine(status), "f_thread_create", F_true);
+ }
+ }
+ else {
+ status = controller_rule_process_do(options_force, process);
+
+ if (status == F_child || F_status_set_fine(status) == F_interrupt) {
+ f_thread_unlock(&process->active);
+
+ return status;
+ }
+ }
+ }
+
+ if (!action || F_status_is_error(status) && (process->state == controller_process_state_active_e || process->state == controller_process_state_busy_e)) {
+ {
+ status_lock = controller_lock_write_process(process, global.thread, &process->lock);
+
+ if (F_status_is_error(status_lock)) {
+ controller_lock_print_error_critical(global.main->error, F_status_set_fine(status_lock), F_false, global.thread);
+
+ f_thread_unlock(&process->active);
+
+ return status_lock;
+ }
+ }
+
+ if (!action || (options_force & controller_process_option_asynchronous_d)) {
+ process->state = controller_process_state_done_e;
+ }
+ else {
+ process->state = controller_process_state_idle_e;
+ }
+
+ f_thread_mutex_lock(&process->wait_lock);
+ f_thread_condition_signal_all(&process->wait);
+ f_thread_mutex_unlock(&process->wait_lock);
+
+ f_thread_unlock(&process->lock);
+ }
+
+ f_thread_unlock(&process->active);
+
+ if (F_status_is_error(status)) {
+ return status;
+ }
+
+ return F_none;
+ }
+#endif // _di_controller_rule_process_begin_
+
+#ifndef _di_controller_rule_process_do_
+ f_status_t controller_rule_process_do(const uint8_t options_force, controller_process_t * const process) {
+
+ f_status_t status_lock = F_none;
+
+ controller_global_t global = macro_controller_global_t_initialize((controller_main_t *) process->main_data, (controller_setting_t *) process->main_setting, (controller_thread_t *) process->main_thread);
+
+ // The process and active locks shall be held for the duration of this processing (aside from switching between read to/from write).
+ if (options_force & controller_process_option_asynchronous_d) {
+ status_lock = controller_lock_read_process(process, global.thread, &process->active);
+
+ if (F_status_is_error(status_lock)) {
+ controller_lock_print_error_critical(global.main->error, F_status_set_fine(status_lock), F_true, global.thread);
+
+ return status_lock;
+ }
+ }
+
+ status_lock = controller_lock_read_process(process, global.thread, &process->lock);
+
+ if (F_status_is_error(status_lock)) {
+ controller_lock_print_error_critical(global.main->error, F_status_set_fine(status_lock), F_true, global.thread);
+
+ if (options_force & controller_process_option_asynchronous_d) {
+ f_thread_unlock(&process->active);
+ }
+
+ return status_lock;
+ }
+
+ f_status_t status = F_none;
+
+ f_array_length_t id_rule = 0;
+
+ const f_array_length_t used_original_stack = process->stack.used;
+
+ status_lock = controller_lock_read_process(process, global.thread, &global.thread->lock.rule);
+
+ if (F_status_is_error(status_lock)) {
+ controller_lock_print_error_critical(global.main->error, F_status_set_fine(status_lock), F_true, global.thread);
+
+ f_thread_unlock(&process->lock);
+
+ if (options_force & controller_process_option_asynchronous_d) {
+ f_thread_unlock(&process->active);
+ }
+
+ return status_lock;
+ }
+
+ if (controller_rule_find(process->rule.alias, global.setting->rules, &id_rule) == F_true) {
+ f_thread_unlock(&process->lock);
+
+ status_lock = controller_lock_write_process(process, global.thread, &process->lock);
+
+ if (F_status_is_error(status_lock)) {
+ controller_lock_print_error_critical(global.main->error, F_status_set_fine(status_lock), F_false, global.thread);
+
+ f_thread_unlock(&global.thread->lock.rule);
+
+ if (options_force & controller_process_option_asynchronous_d) {
+ f_thread_unlock(&process->active);
+ }
+
+ return status_lock;
+ }
+
+ controller_rule_delete_simple(&process->rule);
+
+ status = controller_rule_copy(global.setting->rules.array[id_rule], &process->rule);
+
+ f_thread_unlock(&process->lock);
+
+ status_lock = controller_lock_read_process(process, global.thread, &process->lock);
+
+ if (F_status_is_error(status_lock)) {
+ controller_lock_print_error_critical(global.main->error, F_status_set_fine(status_lock), F_true, global.thread);
+
+ f_thread_unlock(&global.thread->lock.rule);
+
+ if (options_force & controller_process_option_asynchronous_d) {
+ f_thread_unlock(&process->active);
+ }
+
+ return status_lock;
+ }
+
+ f_thread_unlock(&global.thread->lock.rule);
+
+ if (F_status_is_error(status)) {
+ controller_print_error(global.thread, global.main->error, F_status_set_fine(status), "controller_rule_copy", F_true);
+ }
+ else if (!process->action) {
+
+ // This is a "consider" Action, so do not actually execute the rule.
+ f_thread_unlock(&process->lock);
+
+ if (options_force & controller_process_option_asynchronous_d) {
+ f_thread_unlock(&process->active);
+ }
+
+ return F_process_not;
+ }
+ else {
+ for (f_array_length_t i = 0; i < process->stack.used && controller_thread_is_enabled_process(process, global.thread); ++i) {
+
+ if (process->stack.array[i] == id_rule) {
+ if (global.main->error.verbosity != f_console_verbosity_quiet_e) {
+ controller_lock_print(global.main->error.to, global.thread);
+
+ fl_print_format("%c%[%SThe rule '%]", global.main->error.to.stream, f_string_eol_s[0], global.main->error.context, global.main->error.prefix, global.main->error.context);
+ fl_print_format("%[%Q%]", global.main->error.to.stream, global.main->error.notable, process->rule.alias, global.main->error.notable);
+ fl_print_format("%[' is already on the execution dependency stack, this recursion is prohibited.%]%c", global.main->error.to.stream, global.main->error.context, global.main->error.context, f_string_eol_s[0]);
+
+ controller_rule_print_error_cache(global.main->error, process->cache.action, F_true);
+
+ controller_unlock_print_flush(global.main->error.to, global.thread);
+ }
+
+ // Never continue on circular recursion errors even in simulate mode.
+ status = F_status_set_error(F_recurse);
+
+ break;
+ }
+ } // for
+
+ if (!controller_thread_is_enabled_process(process, global.thread)) {
+ f_thread_unlock(&process->lock);
+
+ if (options_force & controller_process_option_asynchronous_d) {
+ f_thread_unlock(&process->active);
+ }
+
+ return F_status_set_error(F_interrupt);
+ }
+
+ if (F_status_is_error_not(status)) {
+ status = f_type_array_lengths_increase(controller_common_allocation_small_d, &process->stack);
+
+ if (F_status_is_error(status)) {
+ controller_print_error(global.thread, global.main->error, F_status_set_fine(status), "f_type_array_lengths_increase", F_true);
+ }
+ else {
+ f_thread_unlock(&process->lock);
+
+ status_lock = controller_lock_write_process(process, global.thread, &process->lock);
+
+ if (F_status_is_error(status_lock)) {
+ controller_lock_print_error_critical(global.main->error, F_status_set_fine(status_lock), F_false, global.thread);
+
+ if (options_force & controller_process_option_asynchronous_d) {
+ f_thread_unlock(&process->active);
+ }
+
+ return status_lock;
+ }
+
+ process->stack.array[process->stack.used++] = id_rule;
+
+ f_thread_unlock(&process->lock);
+
+ status_lock = controller_lock_read_process(process, global.thread, &process->lock);
+
+ if (F_status_is_error(status_lock)) {
+ controller_lock_print_error_critical(global.main->error, F_status_set_fine(status_lock), F_true, global.thread);
+
+ if (options_force & controller_process_option_asynchronous_d) {
+ f_thread_unlock(&process->active);
+ }
+
+ return status_lock;
+ }
+ }
+ }
+ }
+
+ if (F_status_is_error_not(status)) {
+ status = controller_rule_process(global, process);
+ }
+ }
+ else {
+ f_thread_unlock(&global.thread->lock.rule);
+
+ status = F_status_set_error(F_found_not);
+
+ if (global.main->error.verbosity != f_console_verbosity_quiet_e) {
+ controller_lock_print(global.main->error.to, global.thread);
+
+ controller_rule_item_print_error_rule_not_loaded(global.main->error, process->rule.alias.string);
+ controller_rule_print_error_cache(global.main->error, process->cache.action, F_false);
+
+ controller_unlock_print_flush(global.main->error.to, global.thread);
+ }
+ }
+
+ if (status == F_child) {
+ f_thread_unlock(&process->lock);
+
+ if (options_force & controller_process_option_asynchronous_d) {
+ f_thread_unlock(&process->active);
+ }
+
+ return status;
+ }
+
+ status_lock = controller_lock_write_process(process, global.thread, &global.thread->lock.rule);
+
+ if (F_status_is_error(status_lock)) {
+ controller_lock_print_error_critical(global.main->error, F_status_set_fine(status_lock), F_false, global.thread);
+
+ if (F_status_set_fine(status) != F_lock) {
+ f_thread_unlock(&process->lock);
+ }
+
+ if (options_force & controller_process_option_asynchronous_d) {
+ f_thread_unlock(&process->active);
+ }
+
+ return status_lock;
+ }
+
+ if (F_status_set_fine(status) == F_lock) {
+ if (controller_rule_find(process->rule.alias, global.setting->rules, &id_rule) == F_true) {
+ global.setting->rules.array[id_rule].status[process->action] = status;
+ }
+ }
+
+ f_thread_unlock(&global.thread->lock.rule);
+
+ if (F_status_set_fine(status) != F_lock) {
+ f_thread_unlock(&process->lock);
+ }
+
+ if (F_status_set_fine(status) == F_interrupt || F_status_set_fine(status) == F_lock && !controller_thread_is_enabled_process(process, global.thread)) {
+ if (options_force & controller_process_option_asynchronous_d) {
+ f_thread_unlock(&process->active);
+ }
+
+ return F_status_set_error(F_interrupt);
+ }
+
+ status_lock = controller_lock_write_process(process, global.thread, &process->lock);
+
+ if (F_status_is_error(status_lock)) {
+ controller_lock_print_error_critical(global.main->error, F_status_set_fine(status_lock), F_false, global.thread);
+
+ if (options_force & controller_process_option_asynchronous_d) {
+ f_thread_unlock(&process->active);
+ }
+
+ return status_lock;
+ }
+
+ if (options_force & controller_process_option_asynchronous_d) {
+ process->state = controller_process_state_done_e;
+ }
+ else {
+ process->state = controller_process_state_idle_e;
+ }
+
+ process->stack.used = used_original_stack;
+
+ // inform all things waiting that the process has finished running.
+ f_thread_mutex_lock(&process->wait_lock);
+ f_thread_condition_signal_all(&process->wait);
+ f_thread_mutex_unlock(&process->wait_lock);
+
+ f_thread_unlock(&process->lock);
+
+ if (options_force & controller_process_option_asynchronous_d) {
+ f_thread_unlock(&process->active);
+ }
+
+ if (controller_thread_is_enabled_process(process, global.thread)) {
+ return status;
+ }
+
+ return F_status_set_error(F_interrupt);
+ }
+#endif // _di_controller_rule_process_do_
+
+#ifndef _di_controller_rule_read_
+ f_status_t controller_rule_read(const controller_global_t global, const bool is_normal, const f_string_static_t alias, controller_cache_t * const cache, controller_entry_t * const entry, controller_rule_t * const rule) {
+
+ f_status_t status = F_none;
+
+ bool for_item = F_true;
+
+ for (f_array_length_t i = 0; i < controller_rule_action_type__enum_size_e; ++i) {
+ rule->status[i] = F_known_not;
+ } // for
+
+ rule->timeout_kill = entry->timeout_kill ? entry->timeout_kill : 0;
+ rule->timeout_start = entry->timeout_start ? entry->timeout_start : 0;
+ rule->timeout_stop = entry->timeout_stop ? entry->timeout_stop : 0;
+
+ rule->has = 0;
+ rule->group = 0;
+ rule->user = 0;
+ rule->nice = 0;
+
+ macro_f_time_spec_t_clear(rule->timestamp);
+
+ rule->alias.used = 0;
+ rule->name.used = 0;
+ rule->path.used = 0;
+ rule->script.used = 0;
+
+ rule->define.used = 0;
+ rule->parameter.used = 0;
+ rule->environment.used = 0;
+
+ rule->affinity.used = 0;
+
+ if (rule->capability) {
+ f_capability_delete(&rule->capability);
+ rule->capability = 0;
+ }
+
+ for (f_array_length_t i = 0; i < rule->cgroup.groups.size; ++i) {
+ rule->cgroup.groups.array[i].used = 0;
+ } // for
+
+ rule->cgroup.as_new = F_false;
+ rule->cgroup.path.used = 0;
+ rule->cgroup.groups.used = 0;
+
+ macro_f_control_group_t_clear(rule->cgroup);
+
+ rule->groups.used = 0;
+ rule->limits.used = 0;
+
+ rule->scheduler.policy = 0;
+ rule->scheduler.priority = 0;
+
+ for (f_array_length_t i = 0; i < rule->ons.size; ++i) {
+ rule->ons.array[i].need.used = 0;
+ rule->ons.array[i].want.used = 0;
+ rule->ons.array[i].wish.used = 0;
+ } // for
+
+ rule->ons.used = 0;
+ rule->items.used = 0;
+
+ cache->action.line_item = 0;
+ cache->action.line_action = 0;
+
+ cache->range_action.start = 1;
+ cache->range_action.stop = 0;
+
+ cache->comments.used = 0;
+ cache->delimits.used = 0;
+
+ cache->buffer_file.used = 0;
+ cache->buffer_item.used = 0;
+ cache->buffer_path.used = 0;
+
+ for (f_array_length_t i = 0; i < cache->content_items.used; ++i) {
+ cache->content_items.array[i].used = 0;
+ } // for
+
+ cache->content_items.used = 0;
+ cache->object_items.used = 0;
+
+ cache->action.name_action.used = 0;
+ cache->action.name_file.used = 0;
+ cache->action.name_item.used = 0;
+
+ status = f_string_dynamic_append_nulless(alias, &rule->alias);
+
+ if (F_status_is_error(status)) {
+ controller_print_error(global.thread, global.main->error, F_status_set_fine(status), "f_string_dynamic_append_nulless", F_true);
+ }
+ else {
+ status = f_string_dynamic_terminate_after(&rule->alias);
+
+ if (F_status_is_error(status)) {
+ controller_print_error(global.thread, global.main->error, F_status_set_fine(status), "f_string_dynamic_terminate_after", F_true);
+ }
+ else {
+ status = controller_file_load(global, F_true, controller_rules_s, rule->alias, controller_rule_s, controller_rules_s_length, controller_rule_s_length, cache);
+ }
+ }
+
+ if (F_status_is_error_not(status)) {
+ rule->timestamp = cache->timestamp;
+
+ if (cache->buffer_file.used) {
+ controller_state_interrupt_t custom = macro_controller_state_interrupt_t_initialize(is_normal, global.thread);
+ f_state_t state = macro_f_state_t_initialize(controller_common_allocation_large_d, controller_common_allocation_small_d, 0, &controller_thread_signal_state_fss, 0, (void *) &custom, 0);
+ f_string_range_t range = macro_f_string_range_t_initialize(cache->buffer_file.used);
+
+ status = fll_fss_basic_list_read(cache->buffer_file, state, &range, &cache->object_items, &cache->content_items, &cache->delimits, 0, &cache->comments);
+
+ if (F_status_is_error(status)) {
+ controller_print_error(global.thread, global.main->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)) {
+ controller_print_error(global.thread, global.main->error, F_status_set_fine(status), "fl_fss_apply_delimit", F_true);
+ }
+ }
+ }
+ }
+
+ 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)) {
+ controller_print_error(global.thread, global.main->error, F_status_set_fine(status), "controller_rule_items_increase_by", F_true);
+ }
+ else {
+ f_array_length_t i = 0;
+ f_array_length_t j = 0;
+
+ for (; i < cache->object_items.used; ++i) {
+
+ cache->action.line_item = 0;
+ cache->action.line_action = 0;
+
+ cache->range_action.start = 1;
+ cache->range_action.stop = 0;
+
+ cache->comments.used = 0;
+ cache->delimits.used = 0;
+
+ cache->content_action.used = 0;
+
+ for (j = 0; j < cache->content_actions.used; ++j) {
+ cache->content_actions.array[j].used = 0;
+ } // for
+
+ cache->content_actions.used = 0;
+ cache->object_actions.used = 0;
+
+ cache->buffer_item.used = 0;
+ cache->buffer_path.used = 0;
+
+ cache->action.name_action.used = 0;
+ cache->action.name_item.used = 0;
+
+ for_item = F_true;
+
+ status = f_fss_count_lines(cache->buffer_file, cache->object_items.array[i].start, &cache->action.line_item);
+
+ if (F_status_is_error(status)) {
+ controller_print_error(global.thread, global.main->error, F_status_set_fine(status), "f_fss_count_lines", F_true);
+
+ break;
+ }
+
+ rule->items.array[rule->items.used].line = ++cache->action.line_item;
+
+ status = controller_dynamic_rip_nulless_terminated(cache->buffer_file, cache->object_items.array[i], &cache->action.name_item);
+
+ if (F_status_is_error(status)) {
+ controller_print_error(global.thread, global.main->error, F_status_set_fine(status), "controller_dynamic_rip_nulless_terminated", F_true);
+
+ break;
+ }
+
+ if (fl_string_dynamic_compare_string(controller_setting_s, cache->action.name_item, controller_setting_s_length) == F_equal_to) {
+ rule->items.array[rule->items.used].type = 0;
+ }
+ else if (fl_string_dynamic_compare_string(controller_command_s, cache->action.name_item, controller_command_s_length) == F_equal_to) {
+ rule->items.array[rule->items.used].type = controller_rule_item_type_command_e;
+ }
+ else if (fl_string_dynamic_compare_string(controller_script_s, cache->action.name_item, controller_script_s_length) == F_equal_to) {
+ rule->items.array[rule->items.used].type = controller_rule_item_type_script_e;
+ }
+ else if (fl_string_dynamic_compare_string(controller_service_s, cache->action.name_item, controller_service_s_length) == F_equal_to) {
+ rule->items.array[rule->items.used].type = controller_rule_item_type_service_e;
+ }
+ else if (fl_string_dynamic_compare_string(controller_utility_s, cache->action.name_item, controller_utility_s_length) == F_equal_to) {
+ rule->items.array[rule->items.used].type = controller_rule_item_type_utility_e;
+ }
+ else {
+ if (global.main->warning.verbosity == f_console_verbosity_debug_e) {
+ controller_lock_print(global.main->warning.to, global.thread);
+
+ fl_print_format("%c%[%SUnknown rule item '%]", global.main->warning.to.stream, f_string_eol_s[0], global.main->warning.context, global.main->warning.prefix, global.main->warning.context);
+ fl_print_format("%[%Q%]", global.main->warning.to.stream, global.main->warning.notable, cache->action.name_item, global.main->warning.notable);
+ fl_print_format("%['.%]%c", global.main->warning.to.stream, global.main->warning.context, global.main->warning.context, f_string_eol_s[0]);
+
+ controller_rule_print_error_cache(global.main->warning, cache->action, F_true);
+
+ controller_unlock_print_flush(global.main->warning.to, global.thread);
+ }
+
+ continue;
+ }
+
+ status = f_string_dynamic_partial_append(cache->buffer_file, cache->content_items.array[i].array[0], &cache->buffer_item);
+
+ if (F_status_is_error(status)) {
+ controller_print_error(global.thread, global.main->error, F_status_set_fine(status), "f_string_dynamic_partial_append", F_true);
+
+ break;
+ }
+
+ status = f_string_dynamic_terminate_after(&cache->buffer_item);
+
+ if (F_status_is_error(status)) {
+ controller_print_error(global.thread, global.main->error, F_status_set_fine(status), "f_string_dynamic_terminate_after", F_true);
+
+ break;
+ }
+
+ if (rule->items.array[rule->items.used].type) {
+
+ // Initialize the item with settings.
+ rule->items.array[rule->items.used].with = 0;
+
+ if (entry->session == controller_entry_session_new_e) {
+ rule->items.array[rule->items.used].with |= controller_with_session_new_d;
+ }
+ else {
+ rule->items.array[rule->items.used].with |= controller_with_session_same_d;
+ }
+
+ status = controller_rule_item_read(global, is_normal, cache, &rule->items.array[rule->items.used]);
+ if (F_status_is_error(status)) break;
+
+ ++rule->items.used;
+ }
+ else {
+ for_item = F_false;
+
+ status = controller_rule_setting_read(global, is_normal, *global.setting, cache, rule);
+
+ if (F_status_is_error(status)) {
+ if (F_status_set_fine(status) == F_memory_not) {
+ break;
+ }
+ }
+ }
+ } // for
+ }
+ }
+
+ if (F_status_is_error(status)) {
+ controller_rule_item_print_error(global.thread, global.main->error, cache->action, for_item, F_status_set_fine(status));
+
+ rule->status[0] = controller_status_simplify_error(F_status_set_fine(status));
+
+ return rule->status[0];
+ }
+
+ return F_none;
+ }
+#endif // _di_controller_rule_read_
+
+#ifndef _di_controller_rule_setting_read_
+ f_status_t controller_rule_setting_read(const controller_global_t global, const bool is_normal, const controller_setting_t setting, controller_cache_t * const cache, controller_rule_t * const rule) {
+
+ f_status_t status = F_none;
+ f_status_t status_return = F_none;
+
+ f_string_range_t range = macro_f_string_range_t_initialize(cache->buffer_item.used);
+ f_string_range_t range2 = f_string_range_t_initialize;
+
+ {
+ controller_state_interrupt_t custom = macro_controller_state_interrupt_t_initialize(is_normal, global.thread);
+ f_state_t state = macro_f_state_t_initialize(controller_common_allocation_large_d, controller_common_allocation_small_d, 0, &controller_thread_signal_state_fss, 0, (void *) &custom, 0);
+
+ status = fll_fss_extended_read(cache->buffer_item, state, &range, &cache->object_actions, &cache->content_actions, 0, 0, &cache->delimits, 0);
+ }
+
+ if (F_status_is_error(status)) {
+ controller_rule_print_error(global.thread, global.main->error, cache->action, F_status_set_fine(status), "fll_fss_extended_read", F_true, F_false);
+
+ return status;
+ }
+
+ f_array_length_t path_original_length = 0;
+ f_string_dynamic_t *setting_value = 0;
+ f_string_dynamics_t *setting_values = 0;
+ f_string_maps_t *setting_maps = 0;
+
+ f_array_length_t i = 0;
+ f_array_length_t j = 0;
+ uint8_t type = 0;
+ uint8_t action = 0;
+ bool empty_disallow = F_true;
+
+ // Save the current name item and line number to restore on return.
+ const f_array_length_t line_item = cache->action.line_item;
+ const f_array_length_t length_name_item = cache->action.name_item.used;
+
+ char name_item[length_name_item];
+ name_item[length_name_item] = 0;
+
+ memcpy(name_item, cache->action.name_item.string, length_name_item);
+
+ for (; i < cache->content_actions.used; ++i, type = 0) {
+
+ // The name_action is used to store all setting values for a single setting.
+ cache->action.name_action.used = 0;
+
+ // The name_item is used to store the setting name.
+ cache->action.name_item.used = 0;
+
+ status = f_string_dynamic_partial_append_nulless(cache->buffer_item, cache->object_actions.array[i], &cache->action.name_item);
+
+ if (F_status_is_error(status)) {
+ controller_rule_print_error(global.thread, global.main->error, cache->action, F_status_set_fine(status), "f_string_dynamic_partial_append_nulless", F_true, F_false);
+ }
+ else {
+ status = f_string_dynamic_terminate_after(&cache->action.name_item);
+
+ if (F_status_is_error(status)) {
+ controller_rule_print_error(global.thread, global.main->error, cache->action, F_status_set_fine(status), "f_string_dynamic_terminate_after", F_true, F_false);
+ }
+ }
+
+ if (F_status_is_error(status)) {
+ if (F_status_set_fine(status) == F_memory_not) {
+ status_return = status;
+ break;
+ }
+
+ if (F_status_is_error_not(status_return)) {
+ status_return = status;
+ }
+
+ // Get the current line number within the settings item.
+ cache->action.line_item = line_item;
+ f_fss_count_lines(cache->buffer_item, cache->object_actions.array[i].start, &cache->action.line_item);
+
+ cache->action.line_action = ++cache->action.line_item;
+
+ controller_rule_item_print_error(global.thread, global.main->error, cache->action, F_false, F_status_set_fine(status));
+
+ continue;
+ }
+
+ empty_disallow = F_true;
+
+ if (fl_string_dynamic_compare_string(controller_affinity_s, cache->action.name_item, controller_affinity_s_length) == F_equal_to) {
+ type = controller_rule_setting_type_affinity_e;
+ }
+ else if (fl_string_dynamic_compare_string(controller_capability_s, cache->action.name_item, controller_capability_s_length) == F_equal_to) {
+ type = controller_rule_setting_type_capability_e;
+ }
+ else if (fl_string_dynamic_compare_string(controller_cgroup_s, cache->action.name_item, controller_cgroup_s_length) == F_equal_to) {
+ type = controller_rule_setting_type_cgroup_e;
+ }
+ else if (fl_string_dynamic_compare_string(controller_define_s, cache->action.name_item, controller_define_s_length) == F_equal_to) {
+ type = controller_rule_setting_type_define_e;
+ }
+ else if (fl_string_dynamic_compare_string(controller_environment_s, cache->action.name_item, controller_environment_s_length) == F_equal_to) {
+ type = controller_rule_setting_type_environment_e;
+ empty_disallow = F_false;
+ }
+ else if (fl_string_dynamic_compare_string(controller_group_s, cache->action.name_item, controller_group_s_length) == F_equal_to) {
+ type = controller_rule_setting_type_group_e;
+ }
+ else if (fl_string_dynamic_compare_string(controller_limit_s, cache->action.name_item, controller_limit_s_length) == F_equal_to) {
+ type = controller_rule_setting_type_limit_e;
+ }
+ else if (fl_string_dynamic_compare_string(controller_name_s, cache->action.name_item, controller_name_s_length) == F_equal_to) {
+ type = controller_rule_setting_type_name_e;
+ }
+ else if (fl_string_dynamic_compare_string(controller_nice_s, cache->action.name_item, controller_nice_s_length) == F_equal_to) {
+ type = controller_rule_setting_type_nice_e;
+ }
+ else if (fl_string_dynamic_compare_string(controller_on_s, cache->action.name_item, controller_on_s_length) == F_equal_to) {
+ type = controller_rule_setting_type_on_e;
+ }
+ else if (fl_string_dynamic_compare_string(controller_parameter_s, cache->action.name_item, controller_parameter_s_length) == F_equal_to) {
+ type = controller_rule_setting_type_parameter_e;
+ }
+ else if (fl_string_dynamic_compare_string(controller_path_s, cache->action.name_item, controller_path_s_length) == F_equal_to) {
+ type = controller_rule_setting_type_path_e;
+ }
+ else if (fl_string_dynamic_compare_string(controller_scheduler_s, cache->action.name_item, controller_scheduler_s_length) == F_equal_to) {
+ type = controller_rule_setting_type_scheduler_e;
+ }
+ else if (fl_string_dynamic_compare_string(controller_script_s, cache->action.name_item, controller_script_s_length) == F_equal_to) {
+ type = controller_rule_setting_type_script_e;
+ }
+ else if (fl_string_dynamic_compare_string(controller_timeout_s, cache->action.name_item, controller_timeout_s_length) == F_equal_to) {
+ type = controller_rule_setting_type_timeout_e;
+ }
+ else if (fl_string_dynamic_compare_string(controller_user_s, cache->action.name_item, controller_user_s_length) == F_equal_to) {
+ type = controller_rule_setting_type_user_e;
+ }
+ else {
+ if (global.main->warning.verbosity == f_console_verbosity_debug_e) {
+
+ // Get the current line number within the settings item.
+ cache->action.line_item = line_item;
+ f_fss_count_lines(cache->buffer_item, cache->object_actions.array[i].start, &cache->action.line_item);
+
+ cache->action.line_action = ++cache->action.line_item;
+
+ controller_lock_print(global.main->warning.to, global.thread);
+
+ fl_print_format("%c%[%SUnknown rule setting '%]", global.main->warning.to.stream, f_string_eol_s[0], global.main->warning.context, global.main->warning.prefix, global.main->warning.context);
+ fl_print_format("%[%Q%]", global.main->warning.to.stream, global.main->warning.notable, cache->action.name_item, global.main->warning.notable);
+ fl_print_format("%['.%]%c", global.main->warning.to.stream, global.main->warning.context, global.main->warning.context, f_string_eol_s[0]);
+
+ controller_rule_print_error_cache(global.main->warning, cache->action, F_false);
+
+ controller_unlock_print_flush(global.main->warning.to, global.thread);
+ }
+
+ continue;
+ }
+
+ if (cache->content_actions.array[i].used) {
+ 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 = f_string_dynamic_partial_append_nulless(cache->buffer_item, range2, &cache->action.name_action);
+
+ if (F_status_is_error(status)) {
+ controller_rule_print_error(global.thread, global.main->error, cache->action, F_status_set_fine(status), "f_string_dynamic_partial_append_nulless", F_true, F_false);
+ }
+ else {
+ status = f_string_dynamic_terminate_after(&cache->action.name_action);
+
+ if (F_status_is_error(status)) {
+ controller_rule_print_error(global.thread, global.main->error, cache->action, F_status_set_fine(status), "f_string_dynamic_terminate_after", F_true, F_false);
+ }
+ }
+
+ if (F_status_is_error(status)) {
+
+ // Get the current line number within the settings item.
+ cache->action.line_item = line_item;
+ f_fss_count_lines(cache->buffer_item, cache->object_actions.array[i].start, &cache->action.line_item);
+
+ cache->action.line_action = ++cache->action.line_item;
+
+ controller_rule_item_print_error(global.thread, global.main->error, cache->action, F_false, F_status_set_fine(status));
+
+ if (F_status_set_fine(status) == F_memory_not) {
+ status_return = status;
+ break;
+ }
+
+ if (F_status_is_error_not(status_return)) {
+ status_return = status;
+ }
+
+ continue;
+ }
+ }
+ else {
+ if (empty_disallow) {
+ if (global.main->warning.verbosity == f_console_verbosity_debug_e) {
+
+ // Get the current line number within the settings item.
+ cache->action.line_item = line_item;
+ f_fss_count_lines(cache->buffer_item, cache->object_actions.array[i].start, &cache->action.line_item);
+
+ cache->action.line_action = ++cache->action.line_item;
+
+ controller_lock_print(global.main->warning.to, global.thread);
+
+ fl_print_format("%c%[%SEmpty rule setting.%]%c", global.main->warning.to.stream, f_string_eol_s[0], global.main->warning.context, global.main->warning.prefix, global.main->warning.context, f_string_eol_s[0]);
+
+ controller_rule_print_error_cache(global.main->warning, cache->action, F_false);
+
+ controller_unlock_print_flush(global.main->warning.to, global.thread);
+ }
+
+ continue;
+ }
+ }
+
+ if (type == controller_rule_setting_type_affinity_e) {
+ // @todo use sched_getaffinity() to get the available cpus and do not add an invalid cpu to the affinity array.
+
+ if (!cache->content_actions.array[i].used) {
+ controller_rule_setting_read_print_error(global.main->error, "requires one or more Content", i, line_item, global.thread, cache);
+
+ if (F_status_is_error_not(status_return)) {
+ status_return = F_status_set_error(F_valid_not);
+ }
+
+ continue;
+ }
+
+ rule->affinity.used = 0;
+
+ f_number_signed_t number = 0;
+
+ for (j = 0; j < cache->content_actions.array[i].used; ++j, number = 0) {
+
+ // @todo this needs to be in a function such as f_int32s_increase().
+ if (rule->affinity.used + 1 > rule->affinity.size) {
+ f_array_length_t size = rule->affinity.used + controller_common_allocation_small_d;
+
+ if (size > F_array_length_t_size_d) {
+ if (rule->affinity.used + 1 > F_array_length_t_size_d) {
+ status = F_status_set_error(F_array_too_large);
+ }
+ else {
+ size = F_array_length_t_size_d;
+ }
+ }
+
+ if (F_status_is_error_not(status)) {
+ macro_f_int32s_t_resize(status, rule->affinity, size);
+ }
+ }
+
+ if (F_status_is_error(status)) {
+ controller_rule_print_error(global.thread, global.main->error, cache->action, F_status_set_fine(status), "macro_f_int32s_t_resize", F_true, F_false);
+ break;
+ }
+
+ status = fl_conversion_string_to_number_signed(cache->buffer_item.string, cache->content_actions.array[i].array[j], &number);
+
+ if (F_status_set_fine(status) == F_number_positive) {
+ status = fl_conversion_string_to_number_signed(cache->buffer_item.string, controller_range_after_number_sign(cache->buffer_item, cache->content_actions.array[i].array[j]), &number);
+
+ // Restore error on parameter problem.
+ if (F_status_set_fine(status) == F_parameter) {
+ status = F_status_set_error(F_number_positive);
+ }
+ }
+
+ if (F_status_is_error(status)) {
+ status = F_status_set_fine(status);
+
+ if (status == F_data_not || status == F_number || status == F_number_overflow || status == F_number_underflow || status == F_number_negative || status == F_number_decimal) {
+ if (status == F_number_underflow) {
+ controller_rule_setting_read_print_error_with_range(global.main->error, " has an unsupported number", cache->content_actions.array[i].array[j], ", the number is too small for this system", i, line_item, global.thread, cache);
+ }
+ else if (status == F_number_overflow || status == F_number_positive) {
+ controller_rule_setting_read_print_error_with_range(global.main->error, " has an unsupported number", cache->content_actions.array[i].array[j], ", the number is too large for this system", i, line_item, global.thread, cache);
+ }
+ else {
+ controller_rule_setting_read_print_error_with_range(global.main->error, " has an invalid number", cache->content_actions.array[i].array[j], ", only whole numbers are allowed for an affinity value", i, line_item, global.thread, cache);
+ }
+
+ status = F_status_set_error(F_valid_not);
+
+ if (F_status_is_error_not(status_return)) {
+ status_return = status;
+ }
+ }
+ else {
+ controller_rule_print_error(global.thread, global.main->error, cache->action, F_status_set_fine(status), "fl_conversion_string_to_number_signed", F_true, F_false);
+
+ status = F_status_set_error(status);
+
+ if (F_status_is_error_not(status_return)) {
+ status_return = status;
+ }
+ }
+
+ continue;
+ }
+
+ rule->affinity.array[rule->affinity.used++] = number;
+ } // for
+
+ controller_rule_setting_read_print_values(global, controller_affinity_s, i, cache);
+
+ continue;
+ }
+
+ if (type == controller_rule_setting_type_define_e || type == controller_rule_setting_type_parameter_e) {
+ if (cache->content_actions.array[i].used != 2) {
+ controller_rule_setting_read_print_error(global.main->error, "requires exactly two Content", i, line_item, global.thread, cache);
+
+ if (F_status_is_error_not(status_return)) {
+ status_return = F_status_set_error(F_valid_not);
+ }
+
+ continue;
+ }
+
+ if (type == controller_rule_setting_type_define_e) {
+ setting_maps = &rule->define;
+ }
+ else if (type == controller_rule_setting_type_parameter_e) {
+ setting_maps = &rule->parameter;
+ }
+
+ status = f_string_maps_increase(controller_common_allocation_small_d, setting_maps);
+
+ if (F_status_is_error(status)) {
+ controller_rule_print_error(global.thread, global.main->error, cache->action, F_status_set_fine(status), "f_string_maps_increase", F_true, F_false);
+
+ if (F_status_set_fine(status) == F_memory_not) {
+ status_return = status;
+ break;
+ }
+
+ if (F_status_is_error_not(status_return)) {
+ status_return = status;
+ }
+
+ // Get the current line number within the settings item.
+ cache->action.line_item = line_item;
+ f_fss_count_lines(cache->buffer_item, cache->object_actions.array[i].start, &cache->action.line_item);
+
+ cache->action.line_action = ++cache->action.line_item;
+
+ controller_rule_item_print_error(global.thread, global.main->error, cache->action, F_false, F_status_set_fine(status));
+
+ continue;
+ }
+
+ setting_maps->array[setting_maps->used].name.used = 0;
+ setting_maps->array[setting_maps->used].value.used = 0;
+
+ status = f_string_dynamic_partial_append_nulless(cache->buffer_item, cache->content_actions.array[i].array[0], &setting_maps->array[setting_maps->used].name);
+
+ if (F_status_is_error(status)) {
+ controller_rule_print_error(global.thread, global.main->error, cache->action, F_status_set_fine(status), "f_string_dynamic_partial_append_nulless", F_true, F_false);
+
+ if (F_status_set_fine(status) == F_memory_not) {
+ status_return = status;
+ break;
+ }
+
+ if (F_status_is_error_not(status_return)) {
+ status_return = status;
+ }
+
+ // Get the current line number within the settings item.
+ cache->action.line_item = line_item;
+ f_fss_count_lines(cache->buffer_item, cache->object_actions.array[i].start, &cache->action.line_item);
+
+ cache->action.line_action = ++cache->action.line_item;
+
+ controller_rule_item_print_error(global.thread, global.main->error, cache->action, F_false, F_status_set_fine(status));
+
+ continue;
+ }
+
+ status = f_string_dynamic_partial_append_nulless(cache->buffer_item, cache->content_actions.array[i].array[1], &setting_maps->array[setting_maps->used].value);
+
+ if (F_status_is_error(status)) {
+ controller_rule_print_error(global.thread, global.main->error, cache->action, F_status_set_fine(status), "f_string_dynamic_partial_append_nulless", F_true, F_false);
+ }
+ else {
+ status = f_string_dynamic_terminate_after(&setting_maps->array[setting_maps->used].value);
+
+ if (F_status_is_error(status)) {
+ controller_rule_print_error(global.thread, global.main->error, cache->action, F_status_set_fine(status), "f_string_dynamic_terminate_after", F_true, F_false);
+ }
+ }
+
+ if (F_status_is_error(status)) {
+ if (F_status_set_fine(status) == F_memory_not) {
+ status_return = status;
+
+ break;
+ }
+
+ if (F_status_is_error_not(status_return)) {
+ status_return = status;
+ }
+
+ // Get the current line number within the settings item.
+ cache->action.line_item = line_item;
+ f_fss_count_lines(cache->buffer_item, cache->object_actions.array[i].start, &cache->action.line_item);
+
+ cache->action.line_action = ++cache->action.line_item;
+
+ controller_rule_item_print_error(global.thread, global.main->error, cache->action, F_false, F_status_set_fine(status));
+
+ continue;
+ }
+
+ controller_rule_setting_read_print_value(global, type == controller_rule_setting_type_define_e ? controller_define_s : controller_parameter_s, 0, setting_maps->array[setting_maps->used].name, 0);
+
+ ++setting_maps->used;
+
+ continue;
+ }
+
+ if (type == controller_rule_setting_type_cgroup_e) {
+ if (cache->content_actions.array[i].used < 2 || rule->has & controller_rule_has_cgroup_d) {
+ controller_rule_setting_read_print_error(global.main->error, "requires two or more Content", i, line_item, global.thread, cache);
+
+ 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_existing_s, cache->buffer_item, controller_existing_s_length, cache->content_actions.array[i].array[0]) == F_equal_to) {
+ rule->cgroup.as_new = F_false;
+ }
+ else if (fl_string_dynamic_partial_compare_string(controller_new_s, cache->buffer_item, controller_new_s_length, cache->content_actions.array[i].array[0]) == F_equal_to) {
+ rule->cgroup.as_new = F_true;
+ }
+ else {
+ controller_rule_setting_read_print_error_with_range(global.main->error, " has an unknown option", cache->content_actions.array[i].array[0], "", i, line_item, global.thread, cache);
+
+ if (F_status_is_error_not(status_return)) {
+ status_return = F_status_set_error(F_valid_not);
+ }
+
+ continue;
+ }
+
+ rule->cgroup.path.used = 0;
+
+ status = f_string_dynamic_append(global.setting->path_cgroup, &rule->cgroup.path);
+
+ if (F_status_is_error(status)) {
+ controller_rule_print_error(global.thread, global.main->error, cache->action, F_status_set_fine(status), "f_string_dynamic_append", F_true, F_false);
+ }
+ else {
+ rule->cgroup.groups.used = 0;
+
+ for (j = 1; j < cache->content_actions.array[i].used; ++j) {
+
+ status = f_string_dynamics_increase(controller_common_allocation_small_d, &rule->cgroup.groups);
+
+ if (F_status_is_error(status)) {
+ controller_rule_print_error(global.thread, global.main->error, cache->action, F_status_set_fine(status), "f_string_dynamics_increase", F_true, F_false);
+ break;
+ }
+
+ rule->cgroup.groups.array[rule->cgroup.groups.used].used = 0;
+
+ status = f_string_dynamic_partial_append_nulless(cache->buffer_item, cache->content_actions.array[i].array[j], &rule->cgroup.groups.array[rule->cgroup.groups.used]);
+
+ if (F_status_is_error(status)) {
+ controller_rule_print_error(global.thread, global.main->error, cache->action, F_status_set_fine(status), "f_string_dynamic_partial_append_nulless", F_true, F_false);
+ break;
+ }
+
+ ++rule->cgroup.groups.used;
+ } // for
+ }
+
+ if (F_status_is_error(status)) {
+ if (F_status_set_fine(status) == F_memory_not) {
+ status_return = status;
+ break;
+ }
+
+ rule->cgroup.path.used = 0;
+
+ if (F_status_is_error_not(status_return)) {
+ status_return = status;
+ }
+
+ // get the current line number within the settings item.
+ cache->action.line_item = line_item;
+ f_fss_count_lines(cache->buffer_item, cache->object_actions.array[i].start, &cache->action.line_item);
+
+ cache->action.line_action = ++cache->action.line_item;
+
+ controller_rule_item_print_error(global.thread, global.main->error, cache->action, F_false, F_status_set_fine(status));
+
+ continue;
+ }
+
+ rule->has |= controller_rule_has_cgroup_d;
+
+ controller_rule_setting_read_print_values(global, controller_cgroup_s, i, cache);
+
+ continue;
+ }
+
+ if (type == controller_rule_setting_type_limit_e) {
+ if (cache->content_actions.array[i].used != 3) {
+ controller_rule_setting_read_print_error(global.main->error, "requires three Content", i, line_item, global.thread, cache);
+
+ 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_as_s, cache->buffer_item, controller_as_s_length, cache->content_actions.array[i].array[0]) == F_equal_to) {
+ type = controller_resource_limit_type_as_e;
+ }
+ else if (fl_string_dynamic_partial_compare_string(controller_core_s, cache->buffer_item, controller_core_s_length, cache->content_actions.array[i].array[0]) == F_equal_to) {
+ type = controller_resource_limit_type_core_e;
+ }
+ else if (fl_string_dynamic_partial_compare_string(controller_cpu_s, cache->buffer_item, controller_cpu_s_length, cache->content_actions.array[i].array[0]) == F_equal_to) {
+ type = controller_resource_limit_type_cpu_e;
+ }
+ else if (fl_string_dynamic_partial_compare_string(controller_data_s, cache->buffer_item, controller_data_s_length, cache->content_actions.array[i].array[0]) == F_equal_to) {
+ type = controller_resource_limit_type_data_e;
+ }
+ else if (fl_string_dynamic_partial_compare_string(controller_fsize_s, cache->buffer_item, controller_fsize_s_length, cache->content_actions.array[i].array[0]) == F_equal_to) {
+ type = controller_resource_limit_type_fsize_e;
+ }
+ else if (fl_string_dynamic_partial_compare_string(controller_locks_s, cache->buffer_item, controller_locks_s_length, cache->content_actions.array[i].array[0]) == F_equal_to) {
+ type = controller_resource_limit_type_locks_e;
+ }
+ else if (fl_string_dynamic_partial_compare_string(controller_memlock_s, cache->buffer_item, controller_memlock_s_length, cache->content_actions.array[i].array[0]) == F_equal_to) {
+ type = controller_resource_limit_type_memlock_e;
+ }
+ else if (fl_string_dynamic_partial_compare_string(controller_msgqueue_s, cache->buffer_item, controller_msgqueue_s_length, cache->content_actions.array[i].array[0]) == F_equal_to) {
+ type = controller_resource_limit_type_msgqueue_e;
+ }
+ else if (fl_string_dynamic_partial_compare_string(controller_nice_s, cache->buffer_item, controller_nice_s_length, cache->content_actions.array[i].array[0]) == F_equal_to) {
+ type = controller_resource_limit_type_nice_e;
+ }
+ else if (fl_string_dynamic_partial_compare_string(controller_nofile_s, cache->buffer_item, controller_nofile_s_length, cache->content_actions.array[i].array[0]) == F_equal_to) {
+ type = controller_resource_limit_type_nofile_e;
+ }
+ else if (fl_string_dynamic_partial_compare_string(controller_nproc_s, cache->buffer_item, controller_nproc_s_length, cache->content_actions.array[i].array[0]) == F_equal_to) {
+ type = controller_resource_limit_type_nproc_e;
+ }
+ else if (fl_string_dynamic_partial_compare_string(controller_rss_s, cache->buffer_item, controller_rss_s_length, cache->content_actions.array[i].array[0]) == F_equal_to) {
+ type = controller_resource_limit_type_rss_e;
+ }
+ else if (fl_string_dynamic_partial_compare_string(controller_rtprio_s, cache->buffer_item, controller_rtprio_s_length, cache->content_actions.array[i].array[0]) == F_equal_to) {
+ type = controller_resource_limit_type_rtprio_e;
+ }
+ else if (fl_string_dynamic_partial_compare_string(controller_rttime_s, cache->buffer_item, controller_rttime_s_length, cache->content_actions.array[i].array[0]) == F_equal_to) {
+ type = controller_resource_limit_type_rttime_e;
+ }
+ else if (fl_string_dynamic_partial_compare_string(controller_sigpending_s, cache->buffer_item, controller_sigpending_s_length, cache->content_actions.array[i].array[0]) == F_equal_to) {
+ type = controller_resource_limit_type_sigpending_e;
+ }
+ else if (fl_string_dynamic_partial_compare_string(controller_stack_s, cache->buffer_item, controller_stack_s_length, cache->content_actions.array[i].array[0]) == F_equal_to) {
+ type = controller_resource_limit_type_stack_e;
+ }
+ else {
+ if (global.main->error.verbosity == f_console_verbosity_debug_e) {
+
+ // Get the current line number within the settings item.
+ cache->action.line_item = line_item;
+ f_fss_count_lines(cache->buffer_item, cache->object_actions.array[i].start, &cache->action.line_item);
+
+ cache->action.line_action = ++cache->action.line_item;
+
+ controller_lock_print(global.main->error.to, global.thread);
+
+ fl_print_format("%c%[%SUnknown resource limit type '%]", global.main->error.to.stream, f_string_eol_s[0], global.main->error.context, global.main->error.prefix, global.main->error.context);
+ fl_print_format("%[%Q%]", global.main->error.to.stream, global.main->error.notable, cache->action.name_action, global.main->error.notable);
+ fl_print_format("%['.%]%c", global.main->error.to.stream, global.main->error.context, global.main->error.context, f_string_eol_s[0]);
+
+ controller_rule_print_error_cache(global.main->error, cache->action, F_true);
+
+ controller_unlock_print_flush(global.main->error.to, global.thread);
+ }
+
+ if (F_status_is_error_not(status_return)) {
+ status_return = F_status_set_error(F_valid_not);
+ }
+
+ continue;
+ }
+
+ for (j = 0; j < rule->limits.used; ++j) {
+
+ if (type == rule->limits.array[j].type) {
+ if (global.main->error.verbosity != f_console_verbosity_quiet_e) {
+
+ // get the current line number within the settings item.
+ cache->action.line_item = line_item;
+ f_fss_count_lines(cache->buffer_item, cache->object_actions.array[i].start, &cache->action.line_item);
+
+ cache->action.line_action = ++cache->action.line_item;
+
+ controller_lock_print(global.main->error.to, global.thread);
+
+ fl_print_format("%c%[%SThe resource limit type is already specified%]%c", global.main->error.to.stream, f_string_eol_s[0], global.main->error.context, global.main->error.context, f_string_eol_s[0]);
+
+ controller_rule_print_error_cache(global.main->error, cache->action, F_false);
+
+ controller_unlock_print_flush(global.main->error.to, global.thread);
+ }
+
+ status = F_status_set_error(F_valid_not);
+
+ if (F_status_is_error_not(status_return)) {
+ status_return = status;
+ }
+ }
+ } // for
+
+ if (F_status_is_error(status)) continue;
+
+ macro_f_limit_sets_t_increase(status, controller_common_allocation_small_d, rule->limits);
+
+ if (F_status_is_error(status)) {
+ controller_rule_print_error(global.thread, global.main->error, cache->action, F_status_set_fine(status), "f_limit_sets_increase", F_true, F_false);
+
+ if (F_status_set_fine(status) == F_memory_not) {
+ status_return = status;
+ break;
+ }
+
+ if (F_status_is_error_not(status_return)) {
+ status_return = status;
+ }
+
+ // get the current line number within the settings item.
+ cache->action.line_item = line_item;
+ f_fss_count_lines(cache->buffer_item, cache->object_actions.array[i].start, &cache->action.line_item);
+
+ cache->action.line_action = ++cache->action.line_item;
+
+ controller_rule_item_print_error(global.thread, global.main->error, cache->action, F_false, F_status_set_fine(status));
+
+ continue;
+ }
+
+ f_number_signed_t number = 0;
+
+ for (j = 1; j < 3; ++j, number = 0) {
+
+ status = fl_conversion_string_to_number_signed(cache->buffer_item.string, cache->content_actions.array[i].array[j], &number);
+
+ if (F_status_set_fine(status) == F_number_positive) {
+ status = fl_conversion_string_to_number_signed(cache->buffer_item.string, controller_range_after_number_sign(cache->buffer_item, cache->content_actions.array[i].array[j]), &number);
+
+ // Restore error on parameter problem.
+ if (F_status_set_fine(status) == F_parameter) {
+ status = F_status_set_error(F_number_positive);
+ }
+ }
+
+ if (F_status_is_error(status)) {
+ status = F_status_set_fine(status);
+
+ if (status == F_data_not || status == F_number || status == F_number_overflow || status == F_number_underflow || status == F_number_negative || status == F_number_positive || status == F_number_decimal) {
+ if (status == F_number_underflow) {
+ controller_rule_setting_read_print_error_with_range(global.main->error, " has an unsupported number", cache->content_actions.array[i].array[j], ", the number is too small for this system", i, line_item, global.thread, cache);
+ }
+ else if (status == F_number_overflow) {
+ controller_rule_setting_read_print_error_with_range(global.main->error, " has an unsupported number", cache->content_actions.array[i].array[j], ", the number is too large for this system", i, line_item, global.thread, cache);
+ }
+ else {
+ controller_rule_setting_read_print_error_with_range(global.main->error, " has an unsupported number", cache->content_actions.array[i].array[j], ", only whole numbers are allowed for a resource limit value", i, line_item, global.thread, cache);
+ }
+
+ status = F_status_set_error(F_valid_not);
+
+ if (F_status_is_error_not(status_return)) {
+ status_return = status;
+ }
+ }
+ else {
+ controller_rule_print_error(global.thread, global.main->error, cache->action, F_status_set_fine(status), "fl_conversion_string_to_number_signed", F_true, F_false);
+
+ status = F_status_set_error(status);
+
+ if (F_status_is_error_not(status_return)) {
+ status_return = status;
+ }
+ }
+
+ break;
+ }
+
+ if (j == 1) {
+ rule->limits.array[rule->limits.used].value.rlim_cur = number;
+ }
+ else {
+ rule->limits.array[rule->limits.used].value.rlim_max = number;
+ }
+ } // for
+
+ if (F_status_is_error(status)) continue;
+
+ rule->limits.array[rule->limits.used++].type = type;
+
+ controller_rule_setting_read_print_values(global, controller_limit_s, i, cache);
+
+ continue;
+ }
+
+ if (type == controller_rule_setting_type_name_e || type == controller_rule_setting_type_path_e || type == controller_rule_setting_type_script_e) {
+
+ if (type == controller_rule_setting_type_name_e) {
+ setting_value = &rule->name;
+ }
+ else if (type == controller_rule_setting_type_path_e) {
+ setting_value = &rule->path;
+ }
+ else if (type == controller_rule_setting_type_script_e) {
+ setting_value = &rule->script;
+ }
+
+ if (setting_value->used || cache->content_actions.array[i].used != 1) {
+ controller_rule_setting_read_print_error(global.main->error, "requires exactly one Content", i, line_item, global.thread, cache);
+
+ if (F_status_is_error_not(status_return)) {
+ status_return = F_status_set_error(F_valid_not);
+ }
+
+ continue;
+ }
+
+ if (type == controller_rule_setting_type_name_e || type == controller_rule_setting_type_script_e) {
+ status = controller_dynamic_rip_nulless_terminated(cache->buffer_item, cache->content_actions.array[i].array[0], setting_value);
+
+ if (F_status_is_error(status)) {
+ setting_value->used = 0;
+
+ if (F_status_set_fine(status) == F_memory_not) {
+ status_return = status;
+ break;
+ }
+
+ if (F_status_is_error_not(status_return)) {
+ status_return = status;
+ }
+
+ // get the current line number within the settings item.
+ cache->action.line_item = line_item;
+ f_fss_count_lines(cache->buffer_item, cache->object_actions.array[i].start, &cache->action.line_item);
+
+ cache->action.line_action = ++cache->action.line_item;
+
+ controller_rule_item_print_error(global.thread, global.main->error, cache->action, F_false, F_status_set_fine(status));
+
+ continue;
+ }
+
+ status = controller_validate_has_graph(*setting_value);
+
+ if (status == F_false || F_status_set_fine(status) == F_complete_not_utf) {
+ if (global.main->error.verbosity != f_console_verbosity_quiet_e) {
+
+ // get the current line number within the settings item.
+ cache->action.line_item = line_item;
+ f_fss_count_lines(cache->buffer_item, cache->object_actions.array[i].start, &cache->action.line_item);
+
+ cache->action.line_action = ++cache->action.line_item;
+ }
+
+ if (status == F_false) {
+ if (global.main->error.verbosity != f_console_verbosity_quiet_e) {
+ controller_lock_print(global.main->error.to, global.thread);
+
+ fl_print_format("%c%[%SRule setting has an invalid name '%]", global.main->error.to.stream, f_string_eol_s[0], global.main->error.context, global.main->error.prefix, global.main->error.context);
+ fl_print_format("%[%Q%]", global.main->error.to.stream, global.main->error.notable, *setting_value, global.main->error.notable);
+ fl_print_format("%[', there must be at least 1 graph character.%]%c", global.main->error.to.stream, global.main->error.context, global.main->error.context, f_string_eol_s[0]);
+
+ controller_rule_print_error_cache(global.main->error, cache->action, F_false);
+
+ controller_unlock_print_flush(global.main->error.to, global.thread);
+ }
+
+ if (F_status_is_error_not(status_return)) {
+ status_return = F_status_set_error(F_valid_not);
+ }
+ }
+ else {
+
+ // this function should only return F_complete_not_utf on error.
+ controller_rule_print_error(global.thread, global.main->error, cache->action, F_complete_not_utf, "controller_validate_has_graph", F_true, F_false);
+
+ if (F_status_is_error_not(status_return)) {
+ status_return = status;
+ }
+
+ controller_rule_item_print_error(global.thread, global.main->error, cache->action, F_false, F_status_set_fine(status));
+ }
+
+ setting_value->used = 0;
+
+ continue;
+ }
+
+ controller_rule_setting_read_print_value(global, type == controller_rule_setting_type_name_e ? controller_name_s : controller_script_s, 0, *setting_value, 0);
+ }
+ else if (type == controller_rule_setting_type_path_e) {
+ status = f_string_dynamic_partial_append_nulless(cache->buffer_item, cache->content_actions.array[i].array[0], setting_value);
+
+ if (F_status_is_error(status)) {
+ controller_rule_print_error(global.thread, global.main->error, cache->action, F_status_set_fine(status), "f_string_dynamic_partial_append_nulless", F_true, F_false);
+ }
+ else {
+ status = f_string_dynamic_terminate_after(setting_value);
+
+ if (F_status_is_error(status)) {
+ controller_rule_print_error(global.thread, global.main->error, cache->action, F_status_set_fine(status), "f_string_dynamic_terminate_after", F_true, F_false);
+ }
+ }
+
+ if (F_status_is_error(status)) {
+ if (F_status_set_fine(status) == F_memory_not) {
+ status_return = status;
+ break;
+ }
+
+ if (F_status_is_error_not(status_return)) {
+ status_return = status;
+ }
+
+ setting_value->used = 0;
+
+ // get the current line number within the settings item.
+ cache->action.line_item = line_item;
+ f_fss_count_lines(cache->buffer_item, cache->object_actions.array[i].start, &cache->action.line_item);
+
+ cache->action.line_action = ++cache->action.line_item;
+
+ controller_rule_item_print_error(global.thread, global.main->error, cache->action, F_false, F_status_set_fine(status));
+
+ continue;
+ }
+
+ controller_rule_setting_read_print_value(global, controller_path_s, 0, *setting_value, 0);
+ }
+
+ continue;
+ }
+
+ if (type == controller_rule_setting_type_scheduler_e) {
+
+ if (cache->content_actions.array[i].used < 1 || cache->content_actions.array[i].used > 2 || rule->has & controller_rule_has_scheduler_d) {
+ controller_rule_setting_read_print_error(global.main->error, "requires either one or two Content", i, line_item, global.thread, cache);
+
+ 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_batch_s, cache->buffer_item, controller_batch_s_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_deadline_s, cache->buffer_item, controller_deadline_s_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_fifo_s, cache->buffer_item, controller_fifo_s_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_idle_s, cache->buffer_item, controller_idle_s_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_other_s, cache->buffer_item, controller_other_s_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_round_robin_s, cache->buffer_item, controller_round_robin_s_length, cache->content_actions.array[i].array[0]) == F_equal_to) {
+ rule->scheduler.policy = SCHED_RR;
+ rule->scheduler.priority = 49;
+ }
+ else {
+ controller_rule_setting_read_print_error_with_range(global.main->error, " has an unknown scheduler", cache->content_actions.array[i].array[0], "", i, line_item, global.thread, cache);
+
+ 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, cache->content_actions.array[i].array[1], &number);
+
+ if (F_status_set_fine(status) == F_number_positive) {
+ status = fl_conversion_string_to_number_signed(cache->buffer_item.string, controller_range_after_number_sign(cache->buffer_item, cache->content_actions.array[i].array[1]), &number);
+
+ // Restore error on parameter problem.
+ if (F_status_set_fine(status) == F_parameter) {
+ status = F_status_set_error(F_number_positive);
+ }
+ }
+
+ 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 || status == F_number_negative || status == F_number_positive) {
+ if (global.main->error.verbosity != f_console_verbosity_quiet_e) {
+
+ // get the current line number within the settings item.
+ cache->action.line_item = line_item;
+ f_fss_count_lines(cache->buffer_item, cache->object_actions.array[i].start, &cache->action.line_item);
+
+ cache->action.line_action = ++cache->action.line_item;
+
+ controller_lock_print(global.main->error.to, global.thread);
+
+ fl_print_format("%c%[%SRule setting has an invalid number '%]", global.main->error.to.stream, f_string_eol_s[0], global.main->error.context, global.main->error.prefix, global.main->error.context);
+ fl_print_format("%[%/Q%]", global.main->error.to.stream, global.main->error.notable, cache->buffer_item, cache->content_actions.array[i].array[1], global.main->error.notable);
+
+ if (zero_only) {
+ fl_print_format("%[', only%] ", global.main->error.to.stream, global.main->error.context, global.main->error.context);
+ fl_print_format("%[0%]%[ is", global.main->error.to.stream, global.main->error.notable, global.main->error.notable, global.main->error.context);
+ }
+ else {
+ fl_print_format("%[', only the whole numbers inclusively between%] ", global.main->error.to.stream, global.main->error.context, global.main->error.context);
+ fl_print_format("%[1%] %[and%] ", global.main->error.to.stream, global.main->error.notable, global.main->error.notable, global.main->error.context, global.main->error.context);
+ fl_print_format("%[99%] %[are", global.main->error.to.stream, global.main->error.notable, global.main->error.notable, global.main->error.context);
+ }
+
+ fl_print_format(" allowed for the designated scheduler.%]%c", global.main->error.to.stream, global.main->error.context, global.main->error.context, f_string_eol_s[0]);
+
+ controller_rule_print_error_cache(global.main->error, cache->action, F_false);
+
+ controller_unlock_print_flush(global.main->error.to, global.thread);
+ }
+
+ if (F_status_is_error_not(status_return)) {
+ status_return = F_status_set_error(F_valid_not);
+ }
+ }
+ else {
+ controller_rule_print_error(global.thread, global.main->error, cache->action, status, "fl_conversion_string_to_number_signed", F_true, F_false);
+ status = F_status_set_error(status);
+
+ if (F_status_is_error_not(status_return)) {
+ status_return = status;
+ }
+ }
+
+ continue;
+ }
+
+ rule->scheduler.priority = number;
+ }
+
+ rule->has |= controller_rule_has_scheduler_d;
+
+ controller_rule_setting_read_print_values(global, controller_scheduler_s, i, cache);
+
+ continue;
+ }
+
+ if (type == controller_rule_setting_type_timeout_e) {
+ if (cache->content_actions.array[i].used != 2) {
+ controller_rule_setting_read_print_error(global.main->error, "requires exactly two Content", i, line_item, global.thread, cache);
+
+ if (F_status_is_error_not(status_return)) {
+ status_return = F_status_set_error(F_valid_not);
+ }
+
+ continue;
+ }
+
+ uint8_t timeout_code = 0;
+
+ if (fl_string_dynamic_partial_compare_string(controller_kill_s, cache->buffer_item, controller_kill_s_length, cache->content_actions.array[i].array[0]) == F_equal_to) {
+ timeout_code = controller_rule_timeout_code_kill_d;
+ }
+ else if (fl_string_dynamic_partial_compare_string(controller_start_s, cache->buffer_item, controller_start_s_length, cache->content_actions.array[i].array[0]) == F_equal_to) {
+ timeout_code = controller_rule_timeout_code_start_d;
+ }
+ else if (fl_string_dynamic_partial_compare_string(controller_stop_s, cache->buffer_item, controller_stop_s_length, cache->content_actions.array[i].array[0]) == F_equal_to) {
+ timeout_code = controller_rule_timeout_code_stop_d;
+ }
+ else {
+ if (global.main->error.verbosity != f_console_verbosity_quiet_e) {
+
+ // Get the current line number within the settings item.
+ cache->action.line_item = line_item;
+ f_fss_count_lines(cache->buffer_item, cache->object_actions.array[i].start, &cache->action.line_item);
+
+ cache->action.line_action = ++cache->action.line_item;
+
+ controller_lock_print(global.main->error.to, global.thread);
+
+ fl_print_format("%c%[%SRule setting's first value has '%]", global.main->error.to.stream, f_string_eol_s[0], global.main->error.context, global.main->error.prefix, global.main->error.context);
+ fl_print_format("%[%/Q%]", global.main->error.to.stream, global.main->error.notable, cache->buffer_item, cache->content_actions.array[i].array[0], global.main->error.notable);
+ fl_print_format("%[' but only supports %s, %s, and %s.%]%c", global.main->error.to.stream, global.main->error.context, controller_kill_s, controller_start_s, controller_stop_s, global.main->error.context, f_string_eol_s[0]);
+
+ controller_rule_print_error_cache(global.main->error, cache->action, F_false);
+
+ controller_unlock_print_flush(global.main->error.to, global.thread);
+ }
+
+ if (F_status_is_error_not(status_return)) {
+ status_return = F_status_set_error(F_valid_not);
+ }
+
+ continue;
+ }
+
+ f_number_unsigned_t number = 0;
+
+ status = fl_conversion_string_to_number_unsigned(cache->buffer_item.string, cache->content_actions.array[i].array[1], &number);
+
+ if (F_status_set_fine(status) == F_number_positive) {
+ status = fl_conversion_string_to_number_unsigned(cache->buffer_item.string, controller_range_after_number_sign(cache->buffer_item, cache->content_actions.array[i].array[1]), &number);
+
+ // Restore error on parameter problem.
+ if (F_status_set_fine(status) == F_parameter) {
+ status = F_status_set_error(F_number_positive);
+ }
+ }
+
+ if (F_status_is_error(status)) {
+ status = F_status_set_fine(status);
+
+ if (status == F_number_overflow) {
+ controller_rule_setting_read_print_error_with_range(global.main->error, " has an unsupported number", cache->content_actions.array[i].array[1], ", the number is too large for this system", i, line_item, global.thread, cache);
+ }
+ else if (status == F_data_not || status == F_number || status == F_number_underflow || status == F_number_negative || status == F_number_positive || status == F_number_decimal) {
+ controller_rule_setting_read_print_error_with_range(global.main->error, " has an invalid number", cache->content_actions.array[i].array[1], ", only positive whole numbers are allowed", i, line_item, global.thread, cache);
+ }
+ else {
+
+ // Get the current line number within the settings item.
+ cache->action.line_item = line_item;
+ f_fss_count_lines(cache->buffer_item, cache->object_actions.array[i].start, &cache->action.line_item);
+
+ cache->action.line_action = ++cache->action.line_item;
+
+ controller_rule_print_error(global.thread, global.main->error, cache->action, status, "fl_conversion_string_to_number_signed", F_true, F_false);
+ }
+
+ if (F_status_is_error_not(status_return)) {
+ status_return = F_status_set_error(F_valid_not);
+ }
+ }
+ else {
+ if (timeout_code == controller_rule_timeout_code_kill_d) {
+ rule->timeout_kill = number;
+ }
+ else if (timeout_code == controller_rule_timeout_code_start_d) {
+ rule->timeout_start = number;
+ }
+ else {
+ rule->timeout_stop = number;
+ }
+
+ if (global.main->error.verbosity == f_console_verbosity_debug_e || (global.main->error.verbosity == f_console_verbosity_verbose_e && global.main->parameters[controller_parameter_simulate_e].result == f_console_result_found_e)) {
+ f_string_t name_sub = controller_stop_s;
+
+ if (timeout_code == controller_rule_timeout_code_kill_d) {
+ name_sub = controller_kill_s;
+ }
+ else if (timeout_code == controller_rule_timeout_code_start_d) {
+ name_sub = controller_start_s;
+ }
+
+ cache->action.generic.used = 0;
+
+ status = controller_dynamic_rip_nulless_terminated(cache->buffer_item, cache->content_actions.array[i].array[1], &cache->action.generic);
+
+ if (F_status_is_error(status)) {
+ controller_print_error(global.thread, global.main->error, F_status_set_fine(status), "controller_dynamic_rip_nulless_terminated", F_true);
+
+ break;
+ }
+
+ controller_rule_setting_read_print_value(global, controller_timeout_s, name_sub, cache->action.generic, 0);
+ }
+ }
+
+ continue;
+ }
+
+ if (type == controller_rule_setting_type_capability_e || type == controller_rule_setting_type_nice_e || type == controller_rule_setting_type_user_e) {
+ if (cache->content_actions.array[i].used != 1 || type == controller_rule_setting_type_capability_e && rule->capability || type == controller_rule_setting_type_group_e && (rule->has & controller_rule_has_group_d) || type == controller_rule_setting_type_nice_e && (rule->has & controller_rule_has_nice_d) || type == controller_rule_setting_type_user_e && (rule->has & controller_rule_has_user_d)) {
+ controller_rule_setting_read_print_error(global.main->error, "requires exactly one Content", i, line_item, global.thread, cache);
+
+ if (F_status_is_error_not(status_return)) {
+ status_return = F_status_set_error(F_valid_not);
+ }
+
+ continue;
+ }
+
+ if (type == controller_rule_setting_type_capability_e) {
+ cache->action.generic.used = 0;
+
+ status = f_string_dynamic_partial_append_nulless(cache->buffer_item, cache->content_actions.array[i].array[0], &cache->action.generic);
+
+ if (F_status_is_error(status)) {
+
+ // Get the current line number within the settings item.
+ cache->action.line_item = line_item;
+ f_fss_count_lines(cache->buffer_item, cache->object_actions.array[i].start, &cache->action.line_item);
+
+ cache->action.line_action = ++cache->action.line_item;
+
+ controller_lock_print(global.main->error.to, global.thread);
+
+ controller_rule_print_error(global.thread, global.main->error, cache->action, F_status_set_fine(status), "f_string_dynamic_partial_append_nulless", F_true, F_false);
+
+ controller_rule_print_error_cache(global.main->error, cache->action, F_false);
+
+ controller_unlock_print_flush(global.main->error.to, global.thread);
+
+ if (F_status_set_fine(status) == F_memory_not) {
+ status_return = status;
+ break;
+ }
+
+ if (F_status_is_error_not(status_return)) {
+ status_return = F_status_set_error(F_valid_not);
+ }
+ }
+
+ status = f_string_dynamic_terminate_after(&cache->action.generic);
+
+ if (F_status_is_error(status)) {
+
+ // Get the current line number within the settings item.
+ cache->action.line_item = line_item;
+ f_fss_count_lines(cache->buffer_item, cache->object_actions.array[i].start, &cache->action.line_item);
+
+ cache->action.line_action = ++cache->action.line_item;
+
+ controller_lock_print(global.main->error.to, global.thread);
+
+ controller_rule_print_error(global.thread, global.main->error, cache->action, F_status_set_fine(status), "f_string_dynamic_terminate_after", F_true, F_false);
+
+ controller_rule_print_error_cache(global.main->error, cache->action, F_false);
+
+ controller_unlock_print_flush(global.main->error.to, global.thread);
+
+ if (F_status_set_fine(status) == F_memory_not) {
+ status_return = status;
+ break;
+ }
+
+ if (F_status_is_error_not(status_return)) {
+ status_return = F_status_set_error(F_valid_not);
+ }
+ }
+
+ status = f_capability_from_text(cache->action.generic.string, &rule->capability);
+
+ if (F_status_is_error(status) && F_status_set_fine(status) != F_supported_not) {
+ if (F_status_set_fine(status) == F_memory_not) {
+
+ // Get the current line number within the settings item.
+ cache->action.line_item = line_item;
+ f_fss_count_lines(cache->buffer_item, cache->object_actions.array[i].start, &cache->action.line_item);
+
+ cache->action.line_action = ++cache->action.line_item;
+
+ controller_lock_print(global.main->error.to, global.thread);
+
+ controller_rule_print_error(global.thread, global.main->error, cache->action, F_status_set_fine(status), "f_capability_from_text", F_true, F_false);
+
+ controller_rule_print_error_cache(global.main->error, cache->action, F_false);
+
+ controller_unlock_print_flush(global.main->error.to, global.thread);
+
+ status_return = status;
+ break;
+ }
+
+ controller_rule_setting_read_print_error(global.main->error, "failed to process the capabilities", i, line_item, global.thread, cache);
+
+ if (F_status_is_error_not(status_return)) {
+ status_return = F_status_set_error(F_valid_not);
+ }
+
+ continue;
+ }
+
+ controller_rule_setting_read_print_value(global, controller_capability_s, 0, cache->action.generic, 0);
+ }
+ else if (type == controller_rule_setting_type_nice_e) {
+ f_number_signed_t number = 0;
+
+ status = fl_conversion_string_to_number_signed(cache->buffer_item.string, cache->content_actions.array[i].array[0], &number);
+
+ if (F_status_set_fine(status) == F_number_positive) {
+ status = fl_conversion_string_to_number_signed(cache->buffer_item.string, controller_range_after_number_sign(cache->buffer_item, cache->content_actions.array[i].array[0]), &number);
+
+ // Restore error on parameter problem.
+ if (F_status_set_fine(status) == F_parameter) {
+ status = F_status_set_error(F_number_positive);
+ }
+ }
+
+ if (F_status_is_error(status) || number < -20 || number > 19) {
+ status = F_status_set_fine(status);
+
+ if (number < -20 || number > 19 || status == F_data_not || status == F_number || status == F_number_overflow || status == F_number_underflow || status == F_number_decimal) {
+ if (global.main->error.verbosity != f_console_verbosity_quiet_e) {
+
+ // Get the current line number within the settings item.
+ cache->action.line_item = line_item;
+ f_fss_count_lines(cache->buffer_item, cache->object_actions.array[i].start, &cache->action.line_item);
+
+ cache->action.line_action = ++cache->action.line_item;
+
+ controller_lock_print(global.main->error.to, global.thread);
+
+ fl_print_format("%c%[%SRule setting has an invalid number '%]", global.main->error.to.stream, f_string_eol_s[0], global.main->error.context, global.main->error.prefix, global.main->error.context);
+ fl_print_format("%[%/Q%]", global.main->error.to.stream, global.main->error.notable, cache->buffer_item, cache->content_actions.array[i].array[0], global.main->error.notable);
+ fl_print_format("%[', only the whole numbers inclusively between%] ", global.main->error.to.stream, global.main->error.context, global.main->error.context);
+ fl_print_format("%[-20%]", global.main->error.to.stream, global.main->error.notable, global.main->error.notable);
+ fl_print_format(" %[and%] ", global.main->error.to.stream, global.main->error.context, global.main->error.context);
+ fl_print_format("%[19%]", global.main->error.to.stream, global.main->error.notable, global.main->error.notable);
+ fl_print_format(" %[are allowed.%]%c", global.main->error.to.stream, global.main->error.context, global.main->error.context, f_string_eol_s[0]);
+
+ controller_rule_print_error_cache(global.main->error, cache->action, F_false);
+
+ controller_unlock_print_flush(global.main->error.to, global.thread);
+ }
+
+ if (F_status_is_error_not(status_return)) {
+ status_return = F_status_set_error(F_valid_not);
+ }
+ }
+ else {
+ controller_rule_print_error(global.thread, global.main->error, cache->action, status, "fl_conversion_string_to_number_signed", F_true, F_false);
+ status = F_status_set_error(status);
+
+ if (F_status_is_error_not(status_return)) {
+ status_return = status;
+ }
+ }
+ }
+ else {
+ rule->nice = number;
+ rule->has |= controller_rule_has_nice_d;
+
+ if (global.main->parameters[controller_parameter_simulate_e].result == f_console_result_found_e || global.main->error.verbosity == f_console_verbosity_verbose_e) {
+ cache->action.generic.used = 0;
+
+ status = f_string_dynamic_partial_append_nulless(cache->buffer_item, cache->content_actions.array[i].array[0], &cache->action.generic);
+
+ if (F_status_is_error_not(status)) {
+ status = f_string_dynamic_terminate_after(&cache->action.generic);
+ }
+
+ if (F_status_is_error(status)) {
+
+ // Get the current line number within the settings item.
+ cache->action.line_item = line_item;
+ f_fss_count_lines(cache->buffer_item, cache->object_actions.array[i].start, &cache->action.line_item);
+
+ cache->action.line_action = ++cache->action.line_item;
+
+ controller_lock_print(global.main->error.to, global.thread);
+
+ controller_rule_print_error(global.thread, global.main->error, cache->action, F_status_set_fine(status), cache->action.generic.used ? "f_string_dynamic_partial_append_nulless" : "f_string_dynamic_terminate_after", F_true, F_false);
+
+ controller_rule_print_error_cache(global.main->error, cache->action, F_false);
+
+ controller_unlock_print_flush(global.main->error.to, global.thread);
+
+ if (F_status_set_fine(status) == F_memory_not) {
+ status_return = status;
+ break;
+ }
+
+ if (F_status_is_error_not(status_return)) {
+ status_return = F_status_set_error(F_valid_not);
+ }
+ }
+
+ if (F_status_is_error_not(status)) {
+ controller_rule_setting_read_print_value(global, controller_nice_s, 0, cache->action.generic, 0);
+ }
+ }
+ }
+ }
+ else if (type == controller_rule_setting_type_user_e) {
+ uid_t number = 0;
+
+ status = controller_get_id_user(cache->buffer_item, cache->content_actions.array[i].array[0], cache, &number);
+
+ if (F_status_is_error(status)) {
+ status = F_status_set_fine(status);
+
+ if (status == F_exist_not) {
+ controller_rule_setting_read_print_error_with_range(global.main->error, " has an invalid user", cache->content_actions.array[i].array[0], ", because no user was found by that name", i, line_item, global.thread, cache);
+ }
+ else if (status == F_number_too_large) {
+ controller_rule_setting_read_print_error_with_range(global.main->error, " has an invalid user", cache->content_actions.array[i].array[0], ", because the given ID is too large", i, line_item, global.thread, cache);
+ }
+ else if (status == F_number) {
+ controller_rule_setting_read_print_error_with_range(global.main->error, " has an invalid user", cache->content_actions.array[i].array[0], ", because the given ID is not a valid supported number", i, line_item, global.thread, cache);
+ }
+ else {
+
+ // Get the current line number within the settings item.
+ cache->action.line_item = line_item;
+ f_fss_count_lines(cache->buffer_item, cache->object_actions.array[i].start, &cache->action.line_item);
+
+ cache->action.line_action = ++cache->action.line_item;
+
+ controller_rule_print_error(global.thread, global.main->error, cache->action, status, "controller_get_id_user", F_true, F_false);
+
+ controller_rule_item_print_error(global.thread, global.main->error, cache->action, F_false, F_status_set_fine(status));
+ }
+
+ if (F_status_is_error_not(status_return)) {
+ status_return = F_status_set_error(status);
+ }
+ }
+ else {
+ rule->user = number;
+ rule->has |= controller_rule_has_user_d;
+
+ if (global.main->error.verbosity == f_console_verbosity_debug_e || (global.main->error.verbosity == f_console_verbosity_verbose_e && global.main->parameters[controller_parameter_simulate_e].result == f_console_result_found_e)) {
+ cache->action.generic.used = 0;
+
+ status = f_string_dynamic_partial_append_nulless(cache->buffer_item, cache->content_actions.array[i].array[0], &cache->action.generic);
+
+ if (F_status_is_error_not(status)) {
+ status = f_string_dynamic_terminate_after(&cache->action.generic);
+ }
+
+ controller_rule_setting_read_print_value(global, controller_user_s, 0, cache->action.generic, 0);
+ }
+ }
+ }
+
+ continue;
+ }
+
+ if (type == controller_rule_setting_type_group_e) {
+ if (!cache->content_actions.array[i].used) {
+ controller_rule_setting_read_print_error(global.main->error, "requires one or more Content", i, line_item, global.thread, cache);
+
+ if (F_status_is_error_not(status_return)) {
+ status_return = F_status_set_error(F_valid_not);
+ }
+
+ continue;
+ }
+
+ gid_t number = 0;
+
+ rule->groups.used = 0;
+
+ for (j = 0; j < cache->content_actions.array[i].used; ++j) {
+
+ macro_f_int32s_t_increase_by(status, rule->groups, controller_common_allocation_small_d)
+
+ if (F_status_is_error(status)) {
+ controller_rule_print_error(global.thread, global.main->error, cache->action, F_status_set_fine(status), "macro_f_array_lengths_t_increase_by", F_true, F_false);
+
+ if (F_status_set_fine(status) == F_memory_not) {
+ status_return = status;
+ break;
+ }
+
+ if (F_status_is_error_not(status_return)) {
+ status_return = status;
+ }
+
+ // Get the current line number within the settings item.
+ cache->action.line_item = line_item;
+ f_fss_count_lines(cache->buffer_item, cache->object_actions.array[i].start, &cache->action.line_item);
+
+ cache->action.line_action = ++cache->action.line_item;
+
+ controller_rule_item_print_error(global.thread, global.main->error, cache->action, F_false, F_status_set_fine(status));
+
+ continue;
+ }
+
+ status = controller_get_id_group(cache->buffer_item, cache->content_actions.array[i].array[j], cache, &number);
+
+ if (F_status_is_error(status)) {
+ status = F_status_set_fine(status);
+
+ if (status == F_exist_not) {
+ controller_rule_setting_read_print_error_with_range(global.main->error, " has an invalid group", cache->content_actions.array[i].array[j], ", because no group was found by that name", i, line_item, global.thread, cache);
+ }
+ else if (status == F_number_too_large) {
+ controller_rule_setting_read_print_error_with_range(global.main->error, " has an invalid group", cache->content_actions.array[i].array[j], ", because the given ID is too large", i, line_item, global.thread, cache);
+ }
+ else if (status == F_number) {
+ controller_rule_setting_read_print_error_with_range(global.main->error, " has an invalid group", cache->content_actions.array[i].array[j], ", because the given ID is not a valid supported number", i, line_item, global.thread, cache);
+ }
+ else {
+
+ // get the current line number within the settings item.
+ cache->action.line_item = line_item;
+ f_fss_count_lines(cache->buffer_item, cache->object_actions.array[i].start, &cache->action.line_item);
+
+ cache->action.line_action = ++cache->action.line_item;
+
+ controller_rule_print_error(global.thread, global.main->error, cache->action, status, "f_account_id_group_by_name", F_true, F_false);
+
+ controller_rule_item_print_error(global.thread, global.main->error, cache->action, F_false, F_status_set_fine(status));
+ }
+
+ if (F_status_is_error_not(status_return)) {
+ status_return = F_status_set_error(status);
+ }
+ }
+ else {
+ if (rule->has & controller_rule_has_group_d) {
+ rule->groups.array[rule->groups.used++] = number;
+ }
+ else {
+ rule->group = number;
+ rule->has |= controller_rule_has_group_d;
+ }
+ }
+ } // for
+
+ controller_rule_setting_read_print_values(global, controller_group_s, i, cache);
+
+ continue;
+ }
+
+ if (type == controller_rule_setting_type_environment_e) {
+ setting_values = &rule->environment;
+
+ for (j = 0; j < cache->content_actions.array[i].used; ++j) {
+
+ status = f_string_dynamics_increase(controller_common_allocation_small_d, setting_values);
+
+ if (F_status_is_error(status)) {
+ controller_rule_print_error(global.thread, global.main->error, cache->action, F_status_set_fine(status), "f_string_dynamics_increase", F_true, F_false);
+
+ if (F_status_set_fine(status) == F_memory_not) {
+ status_return = status;
+ break;
+ }
+
+ if (F_status_is_error_not(status_return)) {
+ status_return = status;
+ }
+
+ // Get the current line number within the settings item.
+ cache->action.line_item = line_item;
+ f_fss_count_lines(cache->buffer_item, cache->object_actions.array[i].start, &cache->action.line_item);
+
+ cache->action.line_action = ++cache->action.line_item;
+
+ controller_rule_item_print_error(global.thread, global.main->error, cache->action, F_false, F_status_set_fine(status));
+
+ continue;
+ }
+
+ setting_values->array[setting_values->used].used = 0;
+
+ status = f_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)) {
+ controller_rule_print_error(global.thread, global.main->error, cache->action, F_status_set_fine(status), "f_string_dynamic_partial_append_nulless", F_true, F_false);
+ }
+ else {
+ status = f_string_dynamic_terminate_after(&setting_values->array[setting_values->used]);
+
+ if (F_status_is_error(status)) {
+ controller_rule_print_error(global.thread, global.main->error, cache->action, F_status_set_fine(status), "f_string_dynamic_terminate_after", F_true, F_false);
+ }
+ }
+
+ if (F_status_is_error(status)) {
+ setting_values->array[setting_values->used].used = 0;
+
+ if (F_status_set_fine(status) == F_memory_not) {
+ status_return = status;
+ break;
+ }
+
+ if (F_status_is_error_not(status_return)) {
+ status_return = status;
+ }
+
+ // Get the current line number within the settings item.
+ cache->action.line_item = line_item;
+ f_fss_count_lines(cache->buffer_item, cache->object_actions.array[i].start, &cache->action.line_item);
+
+ cache->action.line_action = ++cache->action.line_item;
+
+ controller_rule_item_print_error(global.thread, global.main->error, cache->action, F_false, F_status_set_fine(status));
+
+ continue;
+ }
+
+ status = controller_validate_environment_name(setting_values->array[setting_values->used]);
+
+ if (status == F_false || F_status_set_fine(status) == F_complete_not_utf) {
+
+ // Get the current line number within the settings item.
+ cache->action.line_item = line_item;
+ f_fss_count_lines(cache->buffer_item, cache->object_actions.array[i].start, &cache->action.line_item);
+
+ cache->action.line_action = ++cache->action.line_item;
+
+ if (status == F_false) {
+ if (global.main->error.verbosity != f_console_verbosity_quiet_e) {
+ controller_lock_print(global.main->error.to, global.thread);
+
+ fl_print_format("%c%[%SRule setting has an invalid environment variable name '%]", global.main->error.to.stream, f_string_eol_s[0], global.main->error.context, global.main->error.prefix, global.main->error.context);
+ fl_print_format("%[%Q%]", global.main->error.to.stream, global.main->error.notable, setting_values->array[setting_values->used], global.main->error.notable);
+ fl_print_format("%['.%]%c", global.main->error.to.stream, global.main->error.context, global.main->error.context, f_string_eol_s[0]);
+
+ controller_rule_print_error_cache(global.main->error, cache->action, F_false);
+
+ controller_unlock_print_flush(global.main->error.to, global.thread);
+ }
+
+ if (F_status_is_error_not(status_return)) {
+ status_return = F_status_set_error(F_valid_not);
+ }
+ }
+ else {
+
+ // This function should only return F_complete_not_utf on error.
+ controller_rule_print_error(global.thread, global.main->error, cache->action, F_complete_not_utf, "controller_validate_environment_name", F_true, F_false);
+
+ if (F_status_is_error_not(status_return)) {
+ status_return = status;
+ }
+ }
+
+ setting_values->array[setting_values->used].used = 0;
+
+ controller_rule_item_print_error(global.thread, global.main->error, cache->action, F_false, F_status_set_fine(status));
+
+ continue;
+ }
+
+ ++setting_values->used;
+ } // for
+
+ rule->has |= controller_rule_has_environment_d;
+
+ if (cache->content_actions.array[i].used) {
+ controller_rule_setting_read_print_values(global, controller_environment_s, i, cache);
+ }
+ else {
+ if (global.main->error.verbosity == f_console_verbosity_debug_e || (global.main->error.verbosity == f_console_verbosity_verbose_e && global.main->parameters[controller_parameter_simulate_e].result == f_console_result_found_e)) {
+ controller_lock_print(global.main->output.to, global.thread);
+
+ fl_print_format("%cProcessing rule item action '%[%s%]' setting value to an empty set.%c", global.main->output.to.stream, f_string_eol_s[0], global.main->context.set.title, controller_environment_s, global.main->context.set.title, f_string_eol_s[0]);
+
+ controller_unlock_print_flush(global.main->output.to, global.thread);
+ }
+ }
+
+ continue;
+ }
+
+ // The "on" Rule Setting.
+ if (cache->content_actions.array[i].used != 4) {
+ controller_rule_setting_read_print_error(global.main->error, "requires exactly four Content", i, line_item, global.thread, cache);
+
+ 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_freeze_s, cache->buffer_item, controller_freeze_s_length, cache->content_actions.array[i].array[0]) == F_equal_to) {
+ action = controller_rule_action_type_freeze_e;
+ }
+ else if (fl_string_dynamic_partial_compare_string(controller_kill_s, cache->buffer_item, controller_kill_s_length, cache->content_actions.array[i].array[0]) == F_equal_to) {
+ action = controller_rule_action_type_kill_e;
+ }
+ else if (fl_string_dynamic_partial_compare_string(controller_pause_s, cache->buffer_item, controller_pause_s_length, cache->content_actions.array[i].array[0]) == F_equal_to) {
+ action = controller_rule_action_type_pause_e;
+ }
+ else if (fl_string_dynamic_partial_compare_string(controller_reload_s, cache->buffer_item, controller_reload_s_length, cache->content_actions.array[i].array[0]) == F_equal_to) {
+ action = controller_rule_action_type_reload_e;
+ }
+ else if (fl_string_dynamic_partial_compare_string(controller_restart_s, cache->buffer_item, controller_restart_s_length, cache->content_actions.array[i].array[0]) == F_equal_to) {
+ action = controller_rule_action_type_restart_e;
+ }
+ else if (fl_string_dynamic_partial_compare_string(controller_resume_s, cache->buffer_item, controller_resume_s_length, cache->content_actions.array[i].array[0]) == F_equal_to) {
+ action = controller_rule_action_type_resume_e;
+ }
+ else if (fl_string_dynamic_partial_compare_string(controller_start_s, cache->buffer_item, controller_start_s_length, cache->content_actions.array[i].array[0]) == F_equal_to) {
+ action = controller_rule_action_type_start_e;
+ }
+ else if (fl_string_dynamic_partial_compare_string(controller_stop_s, cache->buffer_item, controller_stop_s_length, cache->content_actions.array[i].array[0]) == F_equal_to) {
+ action = controller_rule_action_type_stop_e;
+ }
+ else if (fl_string_dynamic_partial_compare_string(controller_thaw_s, cache->buffer_item, controller_thaw_s_length, cache->content_actions.array[i].array[0]) == F_equal_to) {
+ action = controller_rule_action_type_thaw_e;
+ }
+ else {
+ if (global.main->error.verbosity != f_console_verbosity_quiet_e) {
+
+ // Get the current line number within the settings item.
+ cache->action.line_item = line_item;
+ f_fss_count_lines(cache->buffer_item, cache->object_actions.array[i].start, &cache->action.line_item);
+
+ cache->action.line_action = ++cache->action.line_item;
+
+ f_thread_mutex_lock(&global.thread->lock.print);
+
+ controller_lock_print(global.main->error.to, global.thread);
+
+ fl_print_format("%c%[%SRule setting's second value has '%]", global.main->error.to.stream, f_string_eol_s[0], global.main->error.context, global.main->error.prefix, global.main->error.context);
+ fl_print_format("%[%/Q%]", global.main->error.to.stream, global.main->error.notable, cache->buffer_item, cache->content_actions.array[i].array[1], global.main->error.notable);
+ fl_print_format("%[' but only supports %s, %s, %s, %s, %s", global.main->error.to.stream, global.main->error.context, controller_freeze_s, controller_kill_s, controller_pause_s, controller_reload_s, controller_restart_s);
+ fl_print_format("%s, %s, %s, and %s.%]%c", global.main->error.to.stream, controller_resume_s, controller_start_s, controller_stop_s, controller_thaw_s, global.main->error.context, f_string_eol_s[0]);
+
+ controller_rule_print_error_cache(global.main->error, cache->action, F_false);
+
+ controller_unlock_print_flush(global.main->error.to, global.thread);
+ }
+
+ if (F_status_is_error_not(status_return)) {
+ status_return = F_status_set_error(F_valid_not);
+ }
+
+ continue;
+ }
+
+ for (j = 0; j < rule->ons.used; ++j) {
+ if (rule->ons.array[j].action == action) break;
+ } // for
+
+ if (j == rule->ons.used) {
+ status = controller_rule_ons_increase(&rule->ons);
+ }
+
+ if (F_status_is_error(status)) {
+ controller_rule_print_error(global.thread, global.main->error, cache->action, F_status_set_fine(status), "controller_rule_ons_increase", F_true, F_false);
+ }
+ else {
+ if (fl_string_dynamic_partial_compare_string(controller_need_s, cache->buffer_item, controller_need_s_length, cache->content_actions.array[i].array[1]) == F_equal_to) {
+ setting_values = &rule->ons.array[j].need;
+ }
+ else if (fl_string_dynamic_partial_compare_string(controller_want_s, cache->buffer_item, controller_want_s_length, cache->content_actions.array[i].array[1]) == F_equal_to) {
+ setting_values = &rule->ons.array[j].want;
+ }
+ else if (fl_string_dynamic_partial_compare_string(controller_wish_s, cache->buffer_item, controller_wish_s_length, cache->content_actions.array[i].array[1]) == F_equal_to) {
+ setting_values = &rule->ons.array[j].wish;
+ }
+ else {
+ if (global.main->error.verbosity != f_console_verbosity_quiet_e) {
+
+ // Get the current line number within the settings item.
+ cache->action.line_item = line_item;
+ f_fss_count_lines(cache->buffer_item, cache->object_actions.array[i].start, &cache->action.line_item);
+
+ cache->action.line_action = ++cache->action.line_item;
+
+ controller_lock_print(global.main->error.to, global.thread);
+
+ fl_print_format("%c%[%SRule setting's second value has '%]", global.main->error.to.stream, f_string_eol_s[0], global.main->error.context, global.main->error.prefix, global.main->error.context);
+ fl_print_format("%[%/Q%]", global.main->error.to.stream, global.main->error.notable, cache->buffer_item, cache->content_actions.array[i].array[1], global.main->error.notable);
+ fl_print_format("%[' but only supports %s, %s, and %s.%]%c", global.main->error.to.stream, global.main->error.context, controller_need_s, controller_want_s, controller_wish_s, global.main->error.context, f_string_eol_s[0]);
+
+ controller_rule_print_error_cache(global.main->error, cache->action, F_false);
+
+ controller_unlock_print_flush(global.main->error.to, global.thread);
+ }
+
+ if (F_status_is_error_not(status_return)) {
+ status_return = F_status_set_error(F_valid_not);
+ }
+
+ continue;
+ }
+
+ status = f_string_dynamics_increase_by(controller_common_allocation_small_d, setting_values);
+
+ if (F_status_is_error(status)) {
+ controller_rule_print_error(global.thread, global.main->error, cache->action, F_status_set_fine(status), "f_string_dynamics_increase_by", F_true, F_false);
+ }
+ }
+
+ if (F_status_is_error(status)) {
+ if (F_status_set_fine(status) == F_memory_not) {
+ status_return = status;
+ break;
+ }
+
+ if (F_status_is_error_not(status_return)) {
+ status_return = status;
+ }
+
+ // Get the current line number within the settings item.
+ cache->action.line_item = line_item;
+ f_fss_count_lines(cache->buffer_item, cache->object_actions.array[i].start, &cache->action.line_item);
+
+ cache->action.line_action = ++cache->action.line_item;
+
+ controller_rule_item_print_error(global.thread, global.main->error, cache->action, F_false, F_status_set_fine(status));
+
+ continue;
+ }
+
+ status = controller_rule_id_construct(global, cache->buffer_item, cache->content_actions.array[i].array[2], cache->content_actions.array[i].array[3], &setting_values->array[setting_values->used]);
+
+ if (F_status_is_error(status)) {
+ setting_values->array[setting_values->used].used = 0;
+
+ if (F_status_set_fine(status) == F_memory_not) {
+ status_return = status;
+ break;
+ }
+
+ if (F_status_is_error_not(status_return)) {
+ status_return = status;
+ }
+
+ // Get the current line number within the settings item.
+ cache->action.line_item = line_item;
+ f_fss_count_lines(cache->buffer_item, cache->object_actions.array[i].start, &cache->action.line_item);
+
+ cache->action.line_action = ++cache->action.line_item;
+
+ controller_rule_item_print_error(global.thread, global.main->error, cache->action, F_false, F_status_set_fine(status));
+
+ continue;
+ }
+
+ cache->buffer_path.used = 0;
+
+ status = f_file_name_base(setting_values->array[setting_values->used].string, setting_values->array[setting_values->used + 1].used, &cache->buffer_path);
+
+ if (F_status_is_error(status)) {
+ setting_values->array[setting_values->used].used = 0;
+
+ controller_rule_print_error(global.thread, global.main->error, cache->action, F_status_set_fine(status), "f_file_name_base", F_true, F_false);
+
+ if (F_status_set_fine(status) == F_memory_not) {
+ status_return = status;
+ break;
+ }
+
+ if (F_status_is_error_not(status_return)) {
+ status_return = status;
+ }
+
+ continue;
+ }
+
+ if (fl_string_dynamic_partial_compare_string(cache->buffer_item.string, cache->buffer_path, cache->buffer_item.used, cache->content_actions.array[i].array[1]) == F_equal_to_not) {
+
+ if (global.main->error.verbosity != f_console_verbosity_quiet_e) {
+ controller_lock_print(global.main->error.to, global.thread);
+
+ fl_print_format("%c%[%SThe rule item action third parameter '%]", global.main->error.to.stream, f_string_eol_s[0], global.main->error.context, global.main->error.prefix, global.main->error.context);
+ fl_print_format("%[%/Q%]", global.main->error.to.stream, global.main->error.notable, cache->buffer_item, cache->content_actions.array[i].array[2], global.main->error.notable);
+ fl_print_format("%[' must be a base path name, such as %un '.%]", global.main->error.to.stream, global.main->error.context, cache->buffer_path.used, global.main->error.context);
+ fl_print_format("%[%Q%]", global.main->error.to.stream, cache->buffer_path, global.main->error.notable);
+ fl_print_format("%['.%]%c", global.main->error.to.stream, global.main->error.context, global.main->error.context, f_string_eol_s[0]);
+
+ controller_unlock_print_flush(global.main->error.to, global.thread);
+ }
+
+ setting_values->array[setting_values->used].used = 0;
+
+ continue;
+ }
+
+ rule->ons.array[j].action = action;
+
+ ++setting_values->used;
+
+ if (j == rule->ons.used) {
+ ++rule->ons.used;
+ }
+
+ if (global.main->error.verbosity == f_console_verbosity_debug_e || (global.main->error.verbosity == f_console_verbosity_verbose_e && global.main->parameters[controller_parameter_simulate_e].result == f_console_result_found_e)) {
+ controller_lock_print(global.main->output.to, global.thread);
+
+ fl_print_format("%cProcessing rule item action '%[%S%]', adding ", global.main->output.to.stream, f_string_eol_s[0], global.main->context.set.title, controller_on_s, global.main->context.set.title);
+ fl_print_format("'%[%/Q%]' of ", global.main->output.to.stream, global.main->context.set.notable, cache->buffer_item, cache->content_actions.array[i].array[1], global.main->context.set.notable);
+ fl_print_format("'%[%/Q%]/", global.main->output.to.stream, global.main->context.set.important, cache->buffer_item, cache->content_actions.array[i].array[2], global.main->context.set.important);
+ fl_print_format("%[%/Q%]'.%c", global.main->output.to.stream, global.main->context.set.important, cache->buffer_item, cache->content_actions.array[i].array[3], global.main->context.set.important, f_string_eol_s[0]);
+
+ controller_unlock_print_flush(global.main->output.to, global.thread);
+ }
+ } // for
+
+ // Restore the current name item and line number, which there should already be enough allocated space for.
+ memcpy(cache->action.name_item.string, name_item, length_name_item);
+
+ cache->action.name_item.string[length_name_item] = 0;
+ cache->action.name_item.used = length_name_item;
+
+ cache->action.line_item = line_item;
+
+ return status_return;
+ }
+#endif // _di_controller_rule_setting_read_
+
+#ifndef _di_controller_rule_validate_
+ void controller_rule_validate(const controller_global_t global, const controller_rule_t rule, const uint8_t action, const uint8_t options, controller_cache_t * const cache) {
+
+ const controller_main_t *main = global.main;
+
+ switch (action) {
+ case controller_rule_action_type_freeze_e:
+ case controller_rule_action_type_kill_e:
+ case controller_rule_action_type_pause_e:
+ case controller_rule_action_type_reload_e:
+ case controller_rule_action_type_restart_e:
+ case controller_rule_action_type_resume_e:
+ case controller_rule_action_type_start_e:
+ case controller_rule_action_type_stop_e:
+ case controller_rule_action_type_thaw_e:
+ break;
+
+ default:
+ if (main->error.verbosity != f_console_verbosity_quiet_e) {
+ controller_lock_print(main->error.to, global.thread);
+
+ fl_print_format("%c%[%SUnsupported action type '%]", main->error.to.stream, f_string_eol_s[0], main->error.context, main->error.prefix ? main->error.prefix : f_string_empty_s, main->error.context);
+ fl_print_format("%[%q%]", main->error.to.stream, main->error.notable, controller_rule_action_type_name(action), main->error.notable);
+ fl_print_format("%[' while attempting to validate rule execution.%]%c", main->error.to.stream, main->error.context, main->error.context, f_string_eol_s[0]);
+
+ controller_rule_print_error_cache(main->error, cache->action, F_true);
+
+ controller_unlock_print_flush(main->error.to, global.thread);
+ }
+
+ return;
+ }
+
+ f_array_length_t i = 0;
+ f_array_length_t j = 0;
+
+ // find at least one of the requested action.
+ {
+ bool missing = F_true;
+
+ for (; i < rule.items.used; ++i) {
+
+ for (j = 0; j < rule.items.array[i].actions.used; ++j) {
+
+ if (!action || rule.items.array[i].actions.array[j].type == action) {
+ missing = F_false;
+ break;
+ }
+ } // for
+ } // for
+
+ if (missing) {
+ controller_lock_print(main->output.to, global.thread);
+
+ if (rule.items.used) {
+ fl_print_format("%cRule '", main->output.to.stream, f_string_eol_s[0]);
+ fl_print_format("%[%Q%]' has no '", main->output.to.stream, main->context.set.title, rule.name, main->context.set.title);
+ fl_print_format("%[%q%]' action to execute and would '", main->output.to.stream, main->context.set.title, controller_rule_action_type_name(action), main->context.set.title);
+ fl_print_format("%[%s%]' because it is '", main->output.to.stream, main->context.set.important, options & controller_process_option_require_d ? controller_fail_s : controller_succeed_s, main->context.set.important);
+ fl_print_format("%[%s%]'.%c", main->output.to.stream, main->context.set.important, options & controller_process_option_require_d ? controller_required_s : controller_optional_s, main->context.set.important, f_string_eol_s[0]);
+ }
+ else {
+ fl_print_format("%cRule '", main->output.to.stream, f_string_eol_s[0]);
+ fl_print_format("%[%Q%]' has no known '", main->output.to.stream, main->context.set.title, rule.name, main->context.set.title);
+ fl_print_format("%[%s %s%]' (such as ", main->output.to.stream, main->context.set.title, controller_rule_s, controller_type_s, main->context.set.title);
+ fl_print_format("'%[%s%]', ", main->output.to.stream, main->context.set.title, controller_command_s, main->context.set.title);
+ fl_print_format("'%[%s%]', ", main->output.to.stream, main->context.set.title, controller_service_s, main->context.set.title);
+ fl_print_format("'%[%s%]', or ", main->output.to.stream, main->context.set.title, controller_script_s, main->context.set.title);
+ fl_print_format("'%[%s%]'", main->output.to.stream, main->context.set.title, controller_utility_s, main->context.set.title);
+ fl_print_format(") and would '%[%s%]' because it is '", main->output.to.stream, main->context.set.important, options & controller_process_option_require_d ? controller_fail_s : controller_succeed_s, main->context.set.important);
+ fl_print_format("%[%s%]'.%c", main->output.to.stream, main->context.set.important, options & controller_process_option_require_d ? controller_required_s : controller_optional_s, main->context.set.important, f_string_eol_s[0]);
+ }
+
+ controller_unlock_print_flush(main->output.to, global.thread);
+ }
+ }
+
+ controller_lock_print(main->output.to, global.thread);
+
+ fl_print_format("%cRule %[%Q%] {%c", main->output.to.stream, f_string_eol_s[0], main->context.set.title, rule.alias, main->context.set.title, f_string_eol_s[0]);
+
+ // name.
+ fl_print_format(" %[%s%] %Q%c", main->output.to.stream, main->context.set.important, controller_name_s, main->context.set.important, rule.name, f_string_eol_s[0]);
+
+ // capability.
+ fl_print_format(" %[%s%] ", main->output.to.stream, main->context.set.important, controller_capability_s, main->context.set.important);
+
+ if (f_capability_supported()) {
+ if (rule.capability) {
+ cache->action.generic.used = 0;
+
+ if (F_status_is_error_not(f_capability_to_text(rule.capability, &cache->action.generic))) {
+ f_print_dynamic_safely(cache->action.generic, main->output.to.stream);
+ }
+ }
+
+ f_print_terminated(f_string_eol_s, main->output.to.stream);
+ }
+ else {
+ fl_print_format("%[(unsupported)%]%c", main->output.to.stream, main->context.set.warning, main->context.set.warning, f_string_eol_s[0]);
+ }
+
+ // control group.
+ fl_print_format(" %[%s%]", main->output.to.stream, main->context.set.important, controller_cgroup_s, main->context.set.important);
+
+ if (rule.has & controller_rule_has_cgroup_d) {
+ fl_print_format(" %s", main->output.to.stream, rule.cgroup.as_new ? controller_new_s : controller_existing_s);
+
+ for (i = 0; i < rule.cgroup.groups.used; ++i) {
+
+ if (rule.cgroup.groups.array[i].used) {
+ fl_print_format(" %Q", main->output.to.stream, rule.cgroup.groups.array[i]);
+ }
+ } // for
+ }
+
+ f_print_terminated(f_string_eol_s, main->output.to.stream);
+
+ // how.
+ fl_print_format(" %[%s%] %s%c", main->output.to.stream, main->context.set.important, controller_how_s, main->context.set.important, options & controller_process_option_asynchronous_d ? controller_asynchronous_s : controller_synchronous_s, f_string_eol_s[0]);
+
+ // nice.
+ fl_print_format(" %[%s%]", main->output.to.stream, main->context.set.important, controller_nice_s, main->context.set.important);
+
+ if (rule.has & controller_rule_has_nice_d) {
+ fl_print_format(" %i", main->output.to.stream, rule.nice);
+ }
+
+ f_print_terminated(f_string_eol_s, main->output.to.stream);
+
+ // scheduler.
+ fl_print_format(" %[%s%]", main->output.to.stream, main->context.set.important, controller_scheduler_s, main->context.set.important);
+
+ if (rule.has & controller_rule_has_scheduler_d) {
+ f_string_t policy = "";
+
+ if (rule.scheduler.policy == SCHED_BATCH) {
+ policy = controller_batch_s;
+ }
+ else if (rule.scheduler.policy == SCHED_DEADLINE) {
+ policy = controller_deadline_s;
+ }
+ else if (rule.scheduler.policy == SCHED_FIFO) {
+ policy = controller_fifo_s;
+ }
+ else if (rule.scheduler.policy == SCHED_IDLE) {
+ policy = controller_idle_s;
+ }
+ else if (rule.scheduler.policy == SCHED_OTHER) {
+ policy = controller_other_s;
+ }
+ else if (rule.scheduler.policy == SCHED_RR) {
+ policy = controller_round_robin_s;
+ }
+
+ fl_print_format(" %s %i", main->output.to.stream, policy, rule.scheduler.priority);
+ }
+
+ f_print_terminated(f_string_eol_s, main->output.to.stream);
+
+ // script.
+ fl_print_format(" %[%s%] %Q%c", main->output.to.stream, main->context.set.important, controller_script_s, main->context.set.important, rule.script, f_string_eol_s[0]);
+
+ // user.
+ fl_print_format(" %[%s%]", main->output.to.stream, main->context.set.important, controller_user_s, main->context.set.important);
+
+ if (rule.has & controller_rule_has_user_d) {
+ fl_print_format(" %i", main->output.to.stream, rule.user);
+ }
+
+ f_print_terminated(f_string_eol_s, main->output.to.stream);
+
+ // wait.
+ fl_print_format(" %[%s%] %s%c", main->output.to.stream, main->context.set.important, controller_wait_s, main->context.set.important, options & controller_process_option_wait_d ? controller_yes_s : controller_no_s, f_string_eol_s[0]);
+
+ // affinity.
+ fl_print_format(" %[%s%] {%c", main->output.to.stream, main->context.set.important, controller_affinity_s, main->context.set.important, f_string_eol_s[0]);
+
+ for (i = 0; i < rule.affinity.used; ++i) {
+ fl_print_format(" %i%c", main->output.to.stream, rule.affinity.array[i], f_string_eol_s[0]);
+ } // for
+
+ // define.
+ fl_print_format(" }%c %[%s%] {%c", main->output.to.stream, f_string_eol_s[0], main->context.set.important, controller_define_s, main->context.set.important, f_string_eol_s[0]);
+
+ for (i = 0; i < rule.define.used; ++i) {
+
+ if (rule.define.array[i].name.used && rule.define.array[i].value.used) {
+ fl_print_format(" %Q %[=%] %Q%c", main->output.to.stream, rule.define.array[i].name, main->context.set.important, main->context.set.important, rule.define.array[i].value, f_string_eol_s[0]);
+ }
+ } // for
+
+ // environment.
+ fl_print_format(" }%c %[%s%] {%c", main->output.to.stream, f_string_eol_s[0], main->context.set.important, controller_environment_s, main->context.set.important, f_string_eol_s[0]);
+
+ for (i = 0; i < rule.environment.used; ++i) {
+
+ if (rule.environment.array[i].used) {
+ fl_print_format(" %Q%c", main->output.to.stream, rule.environment.array[i], f_string_eol_s[0]);
+ }
+ } // for
+
+ fl_print_format(" }%c %[%s%] {%c", main->output.to.stream, f_string_eol_s[0], main->context.set.important, controller_parameter_s, main->context.set.important, f_string_eol_s[0]);
+
+ // parameter.
+ for (i = 0; i < rule.parameter.used; ++i) {
+
+ if (rule.parameter.array[i].name.used && rule.parameter.array[i].value.used) {
+ fl_print_format(" %Q %[=%] %Q%c", main->output.to.stream, rule.parameter.array[i].name, main->context.set.important, main->context.set.important, rule.parameter.array[i].value, f_string_eol_s[0]);
+ }
+ } // for
+
+ // group.
+ fl_print_format(" }%c %[%s%] {%c", main->output.to.stream, f_string_eol_s[0], main->context.set.important, controller_group_s, main->context.set.important, f_string_eol_s[0]);
+
+ if (rule.has & controller_rule_has_group_d) {
+ fl_print_format(" %i%c", main->output.to.stream, rule.group, f_string_eol_s[0]);
+
+ for (i = 0; i < rule.groups.used; ++i) {
+ fl_print_format(" %i%c", main->output.to.stream, rule.groups.array[i], f_string_eol_s[0]);
+ } // for
+ }
+
+ // limit.
+ fl_print_format(" }%c %[%s%] {%c", main->output.to.stream, f_string_eol_s[0], main->context.set.important, controller_limit_s, main->context.set.important, f_string_eol_s[0]);
+
+ for (i = 0; i < rule.limits.used; ++i) {
+ fl_print_format(" %Q %[=%] %un %un%c", main->output.to.stream, controller_rule_setting_limit_type_name(rule.limits.array[i].type), main->context.set.important, main->context.set.important, rule.limits.array[i].value.rlim_cur, rule.limits.array[i].value.rlim_max, f_string_eol_s[0]);
+ } // for
+
+ // on.
+ fl_print_format(" }%c %[%s%] {%c", main->output.to.stream, f_string_eol_s[0], main->context.set.important, controller_on_s, main->context.set.important, f_string_eol_s[0]);
+
+ for (i = 0; i < rule.ons.used; ++i) {
+
+ fl_print_format(" %[%s%] {%c", main->output.to.stream, main->context.set.important, controller_action_s, main->context.set.important, f_string_eol_s[0]);
+
+ {
+ f_string_t action = "";
+
+ if (rule.ons.array[i].action == controller_rule_action_type_freeze_e) {
+ action = controller_freeze_s;
+ }
+ else if (rule.ons.array[i].action == controller_rule_action_type_kill_e) {
+ action = controller_kill_s;
+ }
+ else if (rule.ons.array[i].action == controller_rule_action_type_pause_e) {
+ action = controller_pause_s;
+ }
+ else if (rule.ons.array[i].action == controller_rule_action_type_reload_e) {
+ action = controller_reload_s;
+ }
+ else if (rule.ons.array[i].action == controller_rule_action_type_restart_e) {
+ action = controller_restart_s;
+ }
+ else if (rule.ons.array[i].action == controller_rule_action_type_resume_e) {
+ action = controller_resume_s;
+ }
+ else if (rule.ons.array[i].action == controller_rule_action_type_start_e) {
+ action = controller_start_s;
+ }
+ else if (rule.ons.array[i].action == controller_rule_action_type_stop_e) {
+ action = controller_stop_s;
+ }
+ else if (rule.ons.array[i].action == controller_rule_action_type_thaw_e) {
+ action = controller_thaw_s;
+ }
+
+ fl_print_format(" %[%s%] %s%c", main->output.to.stream, main->context.set.important, controller_type_s, main->context.set.important, action, f_string_eol_s[0]);
+ }
+
+ fl_print_format(" %[%s%] {%c", main->output.to.stream, main->context.set.important, controller_need_s, main->context.set.important, f_string_eol_s[0]);
+
+ for (j = 0; j < rule.ons.array[i].need.used; ++j) {
+
+ if (rule.ons.array[i].need.array[j].used) {
+ fl_print_format(" %Q%c", main->output.to.stream, rule.ons.array[i].need.array[j], f_string_eol_s[0]);
+ }
+ } // for
+
+ fl_print_format(" }%c %[%s%] {%c", main->output.to.stream, f_string_eol_s[0], main->context.set.important, controller_want_s, main->context.set.important, f_string_eol_s[0]);
+
+ for (j = 0; j < rule.ons.array[i].want.used; ++j) {
+
+ if (rule.ons.array[i].want.array[j].used) {
+ fl_print_format(" %Q%c", main->output.to.stream, rule.ons.array[i].want.array[j], f_string_eol_s[0]);
+ }
+ } // for
+
+ fl_print_format(" }%c %[%s%] {%c", main->output.to.stream, f_string_eol_s[0], main->context.set.important, controller_wish_s, main->context.set.important, f_string_eol_s[0]);
+
+ for (j = 0; j < rule.ons.array[i].wish.used; ++j) {
+
+ if (rule.ons.array[i].wish.array[j].used) {
+ fl_print_format(" %Q%c", main->output.to.stream, rule.ons.array[i].wish.array[j], f_string_eol_s[0]);
+ }
+ } // for
+
+ fl_print_format(" }%c }%c", main->output.to.stream, f_string_eol_s[0], f_string_eol_s[0]);
+ } // for
+
+ fl_print_format(" }%c", main->output.to.stream, f_string_eol_s[0]);
+
+ // items.
+ if (rule.items.used) {
+ controller_rule_action_t *action = 0;
+ controller_rule_item_t *item = 0;
+ controller_rule_rerun_item_t *rerun_item = 0;
+ f_string_dynamic_t *parameter = 0;
+
+ f_array_length_t j = 0;
+ f_array_length_t k = 0;
+
+ for (i = 0; i < rule.items.used; ++i) {
+
+ item = &rule.items.array[i];
+
+ fl_print_format(" %[%s%] {%c", main->output.to.stream, main->context.set.important, controller_item_s, main->context.set.important, f_string_eol_s[0]);
+
+ // type.
+ fl_print_format(" %[%s%] %Q%c", main->output.to.stream, main->context.set.important, controller_type_s, main->context.set.important, controller_rule_item_type_name(item->type), f_string_eol_s[0]);
+
+ // pid_file.
+ fl_print_format(" %[%s%]", main->output.to.stream, main->context.set.important, controller_pid_file_s, main->context.set.important);
+ if (item->pid_file.used) {
+ fl_print_format(" %Q", main->output.to.stream, item->pid_file);
+ }
+ f_print_terminated(f_string_eol_s, main->output.to.stream);
+
+ // with.
+ fl_print_format(" %[%s%]", main->output.to.stream, main->context.set.important, controller_with_s, main->context.set.important);
+ if (item->with & controller_with_full_path_d) {
+ fl_print_format(" %s", main->output.to.stream, controller_full_path_s);
+ }
+ if (item->with & controller_with_session_new_d) {
+ fl_print_format(" %s", main->output.to.stream, controller_session_new_s);
+ }
+ if (item->with & controller_with_session_same_d) {
+ fl_print_format(" %s", main->output.to.stream, controller_session_same_s);
+ }
+ f_print_terminated(f_string_eol_s, main->output.to.stream);
+
+ // actions.
+ for (j = 0; j < item->actions.used; ++j) {
+
+ action = &item->actions.array[j];
+
+ fl_print_format(" %[%s%] {%c", main->output.to.stream, main->context.set.important, controller_action_s, main->context.set.important, f_string_eol_s[0]);
+ fl_print_format(" %[%s%] %q%c", main->output.to.stream, main->context.set.important, controller_type_s, main->context.set.important, controller_rule_action_type_name(action->type), f_string_eol_s[0]);
+
+ if (item->type == controller_rule_item_type_script_e || item->type == controller_rule_item_type_utility_e) {
+ fl_print_format(" %[%s%] {%c", main->output.to.stream, main->context.set.important, controller_parameter_s, main->context.set.important, f_string_eol_s[0]);
+
+ parameter = &action->parameters.array[0];
+
+ if (parameter->used) {
+ f_print_terminated(" ", main->output.to.stream);
+
+ for (k = 0; k < parameter->used; ++k) {
+
+ if (parameter->string[k] == f_fss_eol_s[0]) {
+ if (k + 1 < parameter->used) {
+ f_print_terminated(f_string_eol_s, main->output.to.stream);
+ f_print_terminated(" ", main->output.to.stream);
+ }
+ }
+ else {
+ // @fixme change this to handle UTF-8 characters (cannot use f_print_character_safely() as it is not UTF-8 safe as-is).
+ f_print_character_safely(parameter->string[k], main->output.to.stream);
+ }
+ } // for
+
+ f_print_terminated(f_string_eol_s, main->output.to.stream);
+ }
+
+ fl_print_format(" }%c", main->output.to.stream, f_string_eol_s[0]);
+ }
+ else {
+ for (k = 0; k < action->parameters.used; ++k) {
+ fl_print_format(" %[%s%] %Q%c", main->output.to.stream, main->context.set.important, controller_parameter_s, main->context.set.important, action->parameters.array[k], f_string_eol_s[0]);
+ } // for
+ }
+
+ fl_print_format(" }%c", main->output.to.stream, f_string_eol_s[0]);
+ } // for
+
+ // rerun.
+ fl_print_format(" %[%s%] {%c", main->output.to.stream, main->context.set.important, controller_rerun_s, main->context.set.important, f_string_eol_s[0]);
+ for (j = 0; j < controller_rule_action_type_execute__enum_size_e; ++j) {
+
+ for (k = 0; k < 2; ++k) {
+ if (!k && (item->reruns[j].is & controller_rule_rerun_is_failure_d)) {
+ rerun_item = &item->reruns[j].failure;
+ }
+ else if (k && (item->reruns[j].is & controller_rule_rerun_is_success_d)) {
+ rerun_item = &item->reruns[j].success;
+ }
+ else {
+ rerun_item = 0;
+ continue;
+ }
+
+ fl_print_format(" %[", main->output.to.stream, main->context.set.important);
+ switch (j) {
+ case controller_rule_action_type_execute_freeze_e:
+ f_print_terminated(controller_freeze_s, main->output.to.stream);
+ break;
+
+ case controller_rule_action_type_execute_kill_e:
+ f_print_terminated(controller_kill_s, main->output.to.stream);
+ break;
+
+ case controller_rule_action_type_execute_pause_e:
+ f_print_terminated(controller_pause_s, main->output.to.stream);
+ break;
+
+ case controller_rule_action_type_execute_reload_e:
+ f_print_terminated(controller_reload_s, main->output.to.stream);
+ break;
+
+ case controller_rule_action_type_execute_restart_e:
+ f_print_terminated(controller_restart_s, main->output.to.stream);
+ break;
+
+ case controller_rule_action_type_execute_resume_e:
+ f_print_terminated(controller_resume_s, main->output.to.stream);
+ break;
+
+ case controller_rule_action_type_execute_start_e:
+ f_print_terminated(controller_start_s, main->output.to.stream);
+ break;
+
+ case controller_rule_action_type_execute_stop_e:
+ f_print_terminated(controller_stop_s, main->output.to.stream);
+ break;
+
+ case controller_rule_action_type_execute_thaw_e:
+ f_print_terminated(controller_thaw_s, main->output.to.stream);
+ break;
+
+ default:
+ break;
+ }
+
+ fl_print_format("%] %s", main->output.to.stream, main->context.set.important, k ? controller_success_s : controller_failure_s);
+ fl_print_format(" %s %ul %s %ul", main->output.to.stream, controller_delay_s, rerun_item->delay, controller_max_s, rerun_item->max);
+
+ if (!k && (item->reruns[j].is & controller_rule_rerun_is_failure_reset_d) || k && (item->reruns[j].is & controller_rule_rerun_is_success_reset_d)) {
+ fl_print_format(" %s", main->output.to.stream, controller_reset_s);
+ }
+
+ f_print_terminated(f_string_eol_s, main->output.to.stream);
+ } // for
+ } // for
+ fl_print_format(" }%c", main->output.to.stream, f_string_eol_s[0]);
+
+ fl_print_format(" }%c", main->output.to.stream, f_string_eol_s[0]);
+ } // for
+ }
+
+ fl_print_format("}%c", main->output.to.stream, f_string_eol_s[0]);
+
+ controller_unlock_print_flush(main->output.to, global.thread);
+ }
+#endif // _di_controller_rule_validate_
+
+#ifndef _di_controller_rule_wait_all_
+ f_status_t controller_rule_wait_all(const controller_global_t global, const bool is_normal, const bool required, controller_process_t * const caller) {
+
+ f_status_t status_lock = F_none;
+
+ if (caller) {
+ status_lock = controller_lock_read_process(caller, global.thread, &global.thread->lock.process);
+ }
+ else {
+ status_lock = controller_lock_read(is_normal, global.thread, &global.thread->lock.process);
+ }
+
+ if (F_status_is_error(status_lock)) {
+ controller_lock_print_error_critical(global.main->error, F_status_set_fine(status_lock), F_true, global.thread);
+
+ return status_lock;
+ }
+
+ if (!global.thread->processs.used) {
+ f_thread_unlock(&global.thread->lock.process);
+
+ return F_data_not;
+ }
+
+ f_status_t status = F_none;
+
+ bool required_not_run = F_false;
+ bool skip = F_false;
+
+ f_array_length_t i = 0;
+ f_array_length_t j = 0;
+
+ // build a list of what to wait for so that anything new after this point will not be waited for.
+ const f_array_length_t process_total = global.thread->processs.used;
+ controller_process_t *process_list[process_total];
+
+ for (; i < process_total; ++i) {
+ process_list[i] = global.thread->processs.array[i];
+ } // for
+
+ f_thread_unlock(&global.thread->lock.process);
+
+ for (i = 0; i < process_total; ++i) {
+
+ if (caller) {
+ if (!controller_thread_is_enabled_process(caller, global.thread)) break;
+ }
+ else {
+ if (!controller_thread_is_enabled(is_normal, global.thread)) break;
+ }
+
+ // re-establish global process read lock to wait for or protect from the cleanup thread while checking the read process.
+ if (caller) {
+ status_lock = controller_lock_read_process(caller, global.thread, &global.thread->lock.process);
+ }
+ else {
+ status_lock = controller_lock_read(is_normal, global.thread, &global.thread->lock.process);
+ }
+
+ if (F_status_is_error(status_lock)) break;
+
+ if (!process_list[i]) {
+ f_thread_unlock(&global.thread->lock.process);
+
+ continue;
+ }
+
+ if (caller) {
+ status_lock = controller_lock_read_process(caller, global.thread, &process_list[i]->active);
+ }
+ else {
+ status_lock = controller_lock_read(is_normal, global.thread, &process_list[i]->active);
+ }
+
+ if (F_status_is_error(status_lock)) {
+ f_thread_unlock(&global.thread->lock.process);
+
+ break;
+ }
+
+ // once the active lock is obtained, then the main process read lock can be safely released.
+ f_thread_unlock(&global.thread->lock.process);
+
+ if (caller) {
+ if (caller) {
+ status_lock = controller_lock_read_process(caller, global.thread, &global.thread->lock.rule);
+ }
+ else {
+ status_lock = controller_lock_read(is_normal, global.thread, &global.thread->lock.rule);
+ }
+
+ if (F_status_is_error(status_lock)) {
+ f_thread_unlock(&process_list[i]->active);
+
+ break;
+ }
+
+ if (fl_string_dynamic_compare(caller->rule.alias, process_list[i]->rule.alias) == F_equal_to) {
+ f_thread_unlock(&global.thread->lock.rule);
+ f_thread_unlock(&process_list[i]->active);
+
+ continue;
+ }
+
+ skip = F_false;
+
+ for (j = 0; j < caller->stack.used; ++j) {
+
+ if (caller) {
+ if (!controller_thread_is_enabled_process(caller, global.thread)) break;
+ }
+ else {
+ if (!controller_thread_is_enabled(is_normal, global.thread)) break;
+ }
+
+ if (global.thread->processs.array[caller->stack.array[j]] && fl_string_dynamic_compare(process_list[i]->rule.alias, global.thread->processs.array[caller->stack.array[j]]->rule.alias) == F_equal_to) {
+ skip = F_true;
+ }
+
+ if (skip) break;
+ } // for
+
+ f_thread_unlock(&global.thread->lock.rule);
+
+ if (skip) {
+ f_thread_unlock(&process_list[i]->active);
+
+ continue;
+ }
+ }
+
+ if (caller) {
+ status_lock = controller_lock_read_process(caller, global.thread, &process_list[i]->lock);
+ }
+ else {
+ status_lock = controller_lock_read(is_normal, global.thread, &process_list[i]->lock);
+ }
+
+ if (F_status_is_error(status_lock)) {
+ f_thread_unlock(&process_list[i]->active);
+
+ break;
+ }
+
+ if (required) {
+ if (!(process_list[i]->options & controller_process_option_require_d)) {
+ f_thread_unlock(&process_list[i]->lock);
+ f_thread_unlock(&process_list[i]->active);
+
+ continue;
+ }
+ }
+
+ if (!process_list[i]->state || process_list[i]->state == controller_process_state_idle_e || process_list[i]->state == controller_process_state_done_e) {
+
+ if (process_list[i]->state == controller_process_state_done_e) {
+ f_thread_unlock(&process_list[i]->lock);
+
+ if (caller) {
+ status_lock = controller_lock_write_process(process_list[i], global.thread, &process_list[i]->lock);
+ }
+ else {
+ status_lock = controller_lock_write(is_normal, global.thread, &process_list[i]->lock);
+ }
+
+ if (F_status_is_error(status_lock)) {
+ controller_lock_print_error_critical(global.main->error, F_status_set_fine(status_lock), F_false, global.thread);
+
+ f_thread_unlock(&process_list[i]->active);
+
+ return status_lock;
+ }
+
+ if (process_list[i]->state == controller_process_state_done_e) {
+ f_thread_unlock(&process_list[i]->active);
+
+ if (f_thread_lock_write_try(&process_list[i]->active) == F_none) {
+
+ controller_thread_join(&process_list[i]->id_thread);
+
+ process_list[i]->state = controller_process_state_idle_e;
+
+ f_thread_unlock(&process_list[i]->active);
+
+ f_thread_mutex_lock(&process_list[i]->wait_lock);
+ f_thread_condition_signal_all(&process_list[i]->wait);
+ f_thread_mutex_unlock(&process_list[i]->wait_lock);
+ }
+
+ if (caller) {
+ status_lock = controller_lock_read_process(caller, global.thread, &process_list[i]->active);
+ }
+ else {
+ status_lock = controller_lock_read(is_normal, global.thread, &process_list[i]->active);
+ }
+
+ if (F_status_is_error(status_lock)) {
+ f_thread_unlock(&process_list[i]->lock);
+
+ break;
+ }
+ }
+
+ f_thread_unlock(&process_list[i]->lock);
+
+ if (caller) {
+ status_lock = controller_lock_read_process(caller, global.thread, &process_list[i]->lock);
+ }
+ else {
+ status_lock = controller_lock_read(is_normal, global.thread, &process_list[i]->lock);
+ }
+
+ if (F_status_is_error(status_lock)) break;
+ }
+
+ if (process_list[i]->options & controller_process_option_require_d) {
+ if (controller_rule_status_is_error(process_list[i]->action, process_list[i]->rule)) {
+ status = F_status_set_error(F_require);
+
+ f_thread_unlock(&process_list[i]->lock);
+ f_thread_unlock(&process_list[i]->active);
+
+ break;
+ }
+ else if (controller_rule_status_is_available(process_list[i]->action, process_list[i]->rule)) {
+ required_not_run = F_true;
+ }
+ }
+
+ f_thread_unlock(&process_list[i]->lock);
+ f_thread_unlock(&process_list[i]->active);
+
+ if (F_status_set_fine(status) == F_require) break;
+
+ continue;
+ }
+
+ if (!controller_rule_status_is_error(process_list[i]->action, process_list[i]->rule) && (process_list[i]->state == controller_process_state_active_e || process_list[i]->state == controller_process_state_busy_e)) {
+ f_thread_unlock(&process_list[i]->lock);
+
+ status = controller_process_wait(global, process_list[i]);
+
+ if (F_status_set_fine(status) == F_interrupt) {
+ f_thread_unlock(&process_list[i]->active);
+
+ break;
+ }
+
+ if (caller) {
+ status_lock = controller_lock_read_process(caller, global.thread, &process_list[i]->lock);
+ }
+ else {
+ status_lock = controller_lock_read(is_normal, global.thread, &process_list[i]->lock);
+ }
+
+ if (F_status_is_error(status_lock)) {
+ f_thread_unlock(&process_list[i]->active);
+
+ break;
+ }
+
+ if ((process_list[i]->options & controller_process_option_require_d)) {
+ f_thread_unlock(&process_list[i]->lock);
+
+ if (controller_rule_status_is_error(process_list[i]->action, process_list[i]->rule)) {
+ status = F_status_set_error(F_require);
+
+ f_thread_unlock(&process_list[i]->active);
+ break;
+ }
+ }
+ else {
+ f_thread_unlock(&process_list[i]->lock);
+ }
+ }
+ else {
+ f_thread_unlock(&process_list[i]->lock);
+ }
+
+ f_thread_unlock(&process_list[i]->active);
+
+ if (F_status_set_fine(status) == F_interrupt || F_status_set_fine(status) == F_require) break;
+ } // for
+
+ if (F_status_is_error(status_lock)) {
+ controller_lock_print_error_critical(global.main->error, F_status_set_fine(status_lock), F_true, global.thread);
+
+ return status_lock;
+ }
+
+ if (caller) {
+ if (!controller_thread_is_enabled_process(caller, global.thread)) {
+ return F_status_set_error(F_interrupt);
+ }
+ }
+ else {
+ if (!controller_thread_is_enabled(is_normal, global.thread)) {
+ return F_status_set_error(F_interrupt);
+ }
+ }
+
+ if (F_status_set_fine(status) == F_require) {
+ return status;
+ }
+
+ if (required_not_run) {
+ return F_require;
+ }
+
+ return F_none;
+ }
+#endif // _di_controller_rule_wait_all_
+
+#ifndef _di_controller_rule_wait_all_process_type_
+ f_status_t controller_rule_wait_all_process_type(const controller_global_t global, const uint8_t type, const bool required, controller_process_t * const caller) {
+
+ return controller_rule_wait_all(global, type != controller_process_type_exit_e, required, caller);
+ }
+#endif // _di_controller_rule_wait_all_process_type_
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
--- /dev/null
+/**
+ * FLL - Level 3
+ *
+ * Project: Controller
+ * API Version: 0.5
+ * Licenses: lgpl-2.1-or-later
+ */
+#ifndef _PRIVATE_rule_h
+#define _PRIVATE_rule_h
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Get a string representing the rule action method.
+ *
+ * @param type
+ * The rule action type code.
+ *
+ * @return
+ * The string with used > 0 on success.
+ * The string with used == 0 if no match was found.
+ */
+#ifndef _di_controller_rule_action_method_name_
+ extern f_string_static_t controller_rule_action_method_name(const uint8_t type) F_attribute_visibility_internal_d;
+#endif // _di_controller_rule_action_method_name_
+
+/**
+ * Find the location of the Rule by the Rule alias.
+ *
+ * @param alias
+ * The Rule alias to find.
+ * @param rules
+ * The rules to search through.
+ * @param at
+ * The index the rule was found at.
+ * (optional) Set to NULL to disable.
+ *
+ * @return
+ * F_none on success, but the id.used is 0.
+ * F_true on success and rule was found, index is updated.
+ * F_false on success and rule was not found.
+ */
+#ifndef _di_controller_rule_find_
+ extern f_status_t controller_rule_find(const f_string_static_t alias, const controller_rules_t rules, f_array_length_t *at) F_attribute_visibility_internal_d;
+#endif // _di_controller_rule_find_
+
+/**
+ * Read the parameters for some rule action.
+ *
+ * The object and content ranges are merged together (in that order) as the action parameters.
+ *
+ * @param global
+ * The global data.
+ * @param buffer
+ * The buffer containing the content.
+ * @param object
+ * (optional) The range representing where the object is found within the buffer.
+ * Set pointer address to 0 to disable.
+ * @param content
+ * (optional) The ranges representing where the content is found within the buffer.
+ * Set pointer address to 0 to disable.
+ * @param parameters
+ * The processed parameters.
+ *
+ * @return
+ * F_none on success.
+ *
+ * Errors (with error bit) from: f_fss_count_lines().
+ * Errors (with error bit) from: f_string_dynamic_partial_append_nulless().
+ * Errors (with error bit) from: f_string_dynamics_increase().
+ *
+ * @see f_fss_count_lines()
+ * @see f_string_dynamic_partial_append_nulless()
+ * @see f_string_dynamics_increase()
+ */
+#ifndef _di_controller_rule_parameters_read_
+ extern f_status_t controller_rule_parameters_read(const controller_global_t global, const f_string_static_t buffer, f_fss_object_t *object, f_fss_content_t *content, f_string_dynamics_t *parameters) F_attribute_visibility_internal_d;
+#endif // _di_controller_rule_parameters_read_
+
+/**
+ * Convert the action type to an action execute type.
+ *
+ * @param type
+ * The action type to convert from.
+ *
+ * @return
+ * The converted action type, converted into an action execute type.
+ *
+ * The code controller_rule_action_type_execute__enum_size_e is returned for unknown types.
+ *
+ */
+#ifndef _di_controller_rule_action_type_to_action_execute_type_
+ extern uint8_t controller_rule_action_type_to_action_execute_type(const uint8_t type) F_attribute_visibility_internal_d;
+#endif // _di_controller_rule_action_type_to_action_execute_type_
+
+/**
+ * Get a string representing the rule action type.
+ *
+ * @param type
+ * The rule action type code.
+ *
+ * @return
+ * The string with used > 0 on success.
+ * The string with used == 0 if no match was found.
+ */
+#ifndef _di_controller_rule_action_type_name_
+ extern f_string_static_t controller_rule_action_type_name(const uint8_t type) F_attribute_visibility_internal_d;
+#endif // _di_controller_rule_action_type_name_
+
+/**
+ * Get a string representing the rule action execute type.
+ *
+ * @param type
+ * The rule action type execute code.
+ *
+ * @return
+ * The string with used > 0 on success.
+ * The string with used == 0 if no match was found.
+ */
+#ifndef _di_controller_rule_action_type_execute_name_
+ extern f_string_static_t controller_rule_action_type_execute_name(const uint8_t type) F_attribute_visibility_internal_d;
+#endif // _di_controller_rule_action_type_execute_name_
+
+/**
+ * Read the content within the buffer, processing the action (or a set of within a list) for the given item.
+ *
+ * This will automatically increase the size of the actions array as needed.
+ *
+ * @param global
+ * The global data.
+ * @param is_normal
+ * If TRUE, then process as if this operates during a normal operation (entry and control).
+ * If FALSE, then process as if this operates during a an exit operation.
+ * @param type
+ * The action type for this action or set of actions.
+ * @param method
+ * The action method for this action or set of actions.
+ * @param cache
+ * A structure for containing and caching relevant data.
+ * @param item
+ * The processed item.
+ * @param actions
+ * The processed actions.
+ * @param range
+ * The current positions within the buffer being operated on.
+ * This is expected to be set to a position immediately after a valid object read.
+ *
+ * @return
+ * F_none on success.
+ *
+ * Errors (with error bit) from: controller_rule_actions_increase_by().
+ * Errors (with error bit) from: controller_rule_parameters_read().
+ * Errors (with error bit) from: f_fss_count_lines().
+ *
+ * @see controller_rule_actions_increase_by()
+ * @see controller_rule_parameters_read()
+ * @see f_fss_count_lines()
+ */
+#ifndef _di_controller_rule_action_read_
+ extern f_status_t controller_rule_action_read(const controller_global_t global, const bool is_normal, const uint8_t type, const uint8_t method, controller_cache_t * const cache, controller_rule_item_t *item, controller_rule_actions_t *actions, f_string_range_t *range) F_attribute_visibility_internal_d;
+#endif // _di_controller_rule_action_read_
+
+/**
+ * Copy a rule, allocating new space as necessary.
+ *
+ * This does not do any locking or unlocking for the rule data, be sure to lock appropriately before and after calling this.
+ *
+ * @param source
+ * The source rule to copy from.
+ * @param destination
+ * The destination rule to copy to.
+ *
+ * @return
+ * F_none on success.
+ *
+ * Errors (with error bit) from: f_capability_copy().
+ * Errors (with error bit) from: f_control_group_copy().
+ * Errors (with error bit) from: f_limit_sets_copy().
+ * Errors (with error bit) from: f_string_dynamic_append().
+ * Errors (with error bit) from: f_string_dynamics_append().
+ * Errors (with error bit) from: f_string_maps_append().
+ * Errors (with error bit) from: f_type_int32s_append().
+ *
+ * @see f_capability_copy()
+ * @see f_control_group_copy()
+ * @see f_limit_sets_append()
+ * @see f_string_dynamic_append()
+ * @see f_string_dynamics_append()
+ * @see f_string_maps_append()
+ * @see f_type_int32s_append()
+ */
+#ifndef _di_controller_rule_copy_
+ extern f_status_t controller_rule_copy(const controller_rule_t source, controller_rule_t *destination) F_attribute_visibility_internal_d;
+#endif // _di_controller_rule_copy_
+
+/**
+ * Perform an execution of the given rule.
+ *
+ * This requires that a read lock be set on process->lock before being called.
+ *
+ * @param global
+ * The global data.
+ * @param action
+ * The action to perform based on the action type codes.
+ *
+ * Only subset of the action type codes are supported:
+ * - controller_rule_action_type_kill_e
+ * - controller_rule_action_type_pause_e
+ * - controller_rule_action_type_reload_e
+ * - controller_rule_action_type_restart_e
+ * - controller_rule_action_type_resume_e
+ * - controller_rule_action_type_start_e
+ * - controller_rule_action_type_stop_e
+ * @param options
+ * Process options to consider when executing.
+ * If bit controller_process_option_simulate_d, then the rule execution is in simulation mode (printing a message that the rule would be executed but does not execute the rule).
+ * @param process
+ * The process data for processing this rule.
+ *
+ * @return
+ * F_none on success.
+ * F_child on child process exiting.
+ * F_ignore if the rule is unknown and nothing can be done.
+ *
+ * F_failure (with error bit) if failed to execute.
+ * F_interrupt (with error bit) on receiving a process signal, such as an interrupt signal.
+ * F_lock (with error bit) if failed to re-establish read lock on process->lock while returning.
+ *
+ * On success and the rule is run synchronously, then the individual status for the rule is set to F_complete.
+ * On success and the rule is run asynchronously, then the individual status for the rule is set to F_busy.
+ * On failure, the individual status for the rule is set to an appropriate error status.
+ */
+#ifndef _di_controller_rule_execute_
+ extern f_status_t controller_rule_execute(const controller_global_t global, const uint8_t action, const uint8_t options, controller_process_t * const process) F_attribute_visibility_internal_d;
+#endif // _di_controller_rule_execute_
+
+/**
+ * Perform an execution of the given rule in the foreground.
+ *
+ * This requires that a read lock be set on process->lock before being called.
+ *
+ * @param type
+ * The item type code.
+ * @param program
+ * The program to use (such as "bash").
+ * @param arguments
+ * The arguments to pass to the program.
+ * @param options
+ * Process options to consider when executing.
+ * If bit controller_process_option_simulate_d, then the rule execution is in simulation mode (printing a message that the rule would be executed but does not execute the rule).
+ * @param execute_set
+ * The execute parameter and as settings.
+ * @param process
+ * The process data for processing this rule.
+ *
+ * @return
+ * F_none on success.
+ * F_child on child process exiting.
+ *
+ * F_interrupt (with error bit) on receiving a process signal, such as an interrupt signal.
+ * F_lock (with error bit) if failed to re-establish read lock on process->lock while returning.
+ *
+ * Errors (with error bit) from: fll_execute_program().
+ *
+ * @see fll_execute_program()
+ */
+#ifndef _di_controller_rule_execute_foreground_
+ extern f_status_t controller_rule_execute_foreground(const uint8_t type, const f_string_t program, const f_string_statics_t arguments, const uint8_t options, controller_execute_set_t * const execute_set, controller_process_t * const process) F_attribute_visibility_internal_d;
+#endif // _di_controller_rule_execute_foreground_
+
+/**
+ * Perform an execution of the given rule in the foreground or background and creating a PID file.
+ *
+ * This requires that a read lock be set on process->lock before being called.
+ *
+ * When this is synchronous, this will wait for the PID file to be generated before continuing.
+ * When this is asynchronous, this will continue on adding the rule id and action to the asynchronous list.
+ *
+ * @param pid_file
+ * The path to the PID file.
+ * @param type
+ * The item type code.
+ * @param program
+ * The program to use (such as "bash").
+ * @param arguments
+ * The arguments to pass to the program.
+ * @param options
+ * Process options to consider when executing.
+ * If bit controller_process_option_simulate_d, then the rule execution is in simulation mode (printing a message that the rule would be executed but does not execute the rule).
+ * @param with
+ * The "with" option flags.
+ * @param execute_set
+ * The execute parameter and as settings.
+ * @param process
+ * The process data for processing this rule.
+ *
+ * @return
+ * F_none on success.
+ * F_child on child process exiting.
+ *
+ * F_file_found (with error bit) if the PID file already exists.
+ * F_interrupt (with error bit) on receiving a process signal, such as an interrupt signal.
+ * F_lock (with error bit) if failed to re-establish read lock on process->lock while returning.
+ *
+ * Errors (with error bit) from: fll_execute_program().
+ *
+ * @see fll_execute_program()
+ */
+#ifndef _di_controller_rule_execute_pid_with_
+ extern f_status_t controller_rule_execute_pid_with(const f_string_dynamic_t pid_file, const uint8_t type, const f_string_t program, const f_string_statics_t arguments, const uint8_t options, const uint8_t with, controller_execute_set_t * const execute_set, controller_process_t * const process) F_attribute_visibility_internal_d;
+#endif // _di_controller_rule_execute_pid_with_
+
+/**
+ * Determine whether or not an execute rule should be re-run, applying a delay as requested.
+ *
+ * @param action
+ * The action type.
+ * @param process
+ * The process data for processing this rule.
+ * @param item
+ * The rule item being executed.
+ *
+ * @return
+ * A positive number to designate re-run.
+ * 0 to designate do not re-run.
+ * -1 to designate an error from nanosleep(), with errno set to values like:
+ * - EFAULT: Designates that there was a problem copying information from user space.
+ * - EINTR: Consider this having returned F_interrupt.
+ * - EINVAL: Consider this having returned F_status_set_error(F_parameter);
+ * -2 to designate exit due to signal/disabled thread.
+ */
+#ifndef _di_controller_rule_execute_rerun_
+ extern int8_t controller_rule_execute_rerun(const uint8_t action, controller_process_t * const process, controller_rule_item_t * const item) F_attribute_visibility_internal_d;
+#endif // _di_controller_rule_execute_rerun_
+
+/**
+ * Construct an id from two distinct strings found within a single given source.
+ *
+ * @param global
+ * The global data.
+ * @param source
+ * The source string that both the directory and basename are copied from.
+ * @param directory
+ * A range within the source representing the directory part of a rule id.
+ * @param basename
+ * A range within the source representing the basename part of a rule id.
+ * @param alias
+ * The constructed alias.
+ *
+ * @return
+ * F_none on success.
+ *
+ * Errors (with error bit) from: f_string_dynamic_partial_append_nulless().
+ * Errors (with error bit) from: f_string_dynamic_terminate_after().
+ *
+ * @see f_string_append()
+ * @see f_string_dynamic_partial_append_nulless()
+ * @see f_string_dynamic_terminate_after()
+ */
+#ifndef _di_controller_rule_id_construct_
+ extern f_status_t controller_rule_id_construct(const controller_global_t global, const f_string_static_t source, const f_string_range_t directory, const f_string_range_t basename, f_string_dynamic_t * const alias) F_attribute_visibility_internal_d;
+#endif // _di_controller_rule_id_construct_
+
+/**
+ * Check to see if the given Rule has status F_known_not for the given Rule Action.
+ *
+ * The global Rule status is checked for error and any errors on the global Rule status will result in F_false.
+ *
+ * @param action
+ * The Rule Action type.
+ * @param rule
+ * The Rule.
+ *
+ * @return
+ * F_true on available (status is F_known_not).
+ * F_false on unavailable.
+ */
+#ifndef _di_controller_rule_status_is_available_
+ extern f_status_t controller_rule_status_is_available(const uint8_t action, const controller_rule_t rule) F_attribute_visibility_internal_d;
+#endif // _di_controller_rule_status_is_available_
+
+/**
+ * Check to see if the given Rule has status is designated as an error for the given Rule Action.
+ *
+ * The global Rule status is checked for error and any errors on the global Rule status will result in F_true.
+ *
+ * @param action
+ * The Rule Action type.
+ * @param rule
+ * The Rule.
+ *
+ * @return
+ * F_true if status represents an error.
+ * F_false if status does not represent an error.
+ */
+#ifndef _di_controller_rule_status_is_error_
+ extern f_status_t controller_rule_status_is_error(const uint8_t action, const controller_rule_t rule) F_attribute_visibility_internal_d;
+#endif // _di_controller_rule_status_is_error_
+
+/**
+ * Read the content within the buffer, extracting all valid items after determining their type for some rule file.
+ *
+ * This will perform additional FSS read functions as appropriate.
+ *
+ * @param global
+ * The global data.
+ * @param is_normal
+ * If TRUE, then process as if this operates during a normal operation (entry and control).
+ * If FALSE, then process as if this operates during a an exit operation.
+ * @param cache
+ * A structure for containing and caching relevant data.
+ * @param item
+ * The processed item.
+ *
+ * @return
+ * F_none on success.
+ * F_valid_not (with error bit) on invalid data.
+ *
+ * Errors (with error bit) from: f_fss_count_lines().
+ * Errors (with error bit) from: f_string_dynamic_partial_append_nulless().
+ * Errors (with error bit) from: f_string_dynamic_terminate_after().
+ *
+ * @see controller_rule_action_read()
+ * @see f_fss_count_lines()
+ * @see f_string_dynamic_partial_append_nulless()
+ * @see f_string_dynamic_terminate_after()
+ */
+#ifndef _di_controller_rule_item_read_
+ extern f_status_t controller_rule_item_read(const controller_global_t global, const bool is_normal, controller_cache_t * const cache, controller_rule_item_t * const item) F_attribute_visibility_internal_d;
+#endif // _di_controller_rule_item_read_
+
+/**
+ * Get a string representing the rule item type.
+ *
+ * @param type
+ * The rule item type code.
+ *
+ * @return
+ * The string with used > 0 on success.
+ * The string with used == 0 if no match was found.
+ */
+#ifndef _di_controller_rule_item_type_name_
+ extern f_string_static_t controller_rule_item_type_name(const uint8_t type) F_attribute_visibility_internal_d;
+#endif // _di_controller_rule_item_type_name_
+
+/**
+ * Increase the size of the rule 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.
+ *
+ * @param amount
+ * A positive number representing how much to increase the size by.
+ * @param items
+ * The items to resize.
+ *
+ * @return
+ * F_none on success.
+ * F_array_too_large (with error bit) if the resulting new size is bigger than the max array length.
+ *
+ * Errors (with error bit) from: f_memory_resize().
+ *
+ * @see f_memory_resize()
+ */
+#ifndef _di_controller_rule_items_increase_by_
+ extern f_status_t controller_rule_items_increase_by(const f_array_length_t amount, controller_rule_items_t * const items) F_attribute_visibility_internal_d;
+#endif // _di_controller_rule_items_increase_by_
+
+/**
+ * Get a string representing the rule setting limit type.
+ *
+ * @param type
+ * The rule setting limit type code.
+ *
+ * @return
+ * The string with used > 0 on success.
+ * The string with used == 0 if no match was found.
+ */
+#ifndef _di_controller_rule_setting_limit_type_name_
+ extern f_string_static_t controller_rule_setting_limit_type_name(const uint8_t type) F_attribute_visibility_internal_d;
+#endif // di_controller_rule_setting_limit_type_name_
+
+/**
+ * Process and execute the given rule.
+ *
+ * Any dependent rules are processed and executed as per "need", "want", and "wish" rule settings.
+ * All dependent rules must be already loaded, this function will not load any rules.
+ *
+ * This requires that a read lock be set on process->lock before being called.
+ *
+ * This function is recursively called for each "need", "want", and "wish", and has a max recursion length of the max size of the f_array_lengths_t array.
+ *
+ * The rule status will be updated by this function.
+ *
+ * @param global
+ * The global data.
+ * @param process
+ * The process data for processing this rule.
+ *
+ * @return
+ * F_none on success.
+ * F_child on child process exiting.
+ * F_failure on execution failure.
+ *
+ * F_interrupt (with error bit) on receiving a process signal, such as an interrupt signal.
+ * F_lock (with error bit) if failed to re-establish read lock on process->lock while returning.
+ *
+ * Errors (with error bit) from: controller_lock_read().
+ * Errors (with error bit) from: controller_lock_write().
+ */
+#ifndef _di_controller_rule_process_
+ extern f_status_t controller_rule_process(const controller_global_t global, controller_process_t * const process) F_attribute_visibility_internal_d;
+#endif // _di_controller_rule_process_
+
+/**
+ * Synchronously or asynchronously begin processing some rule.
+ *
+ * @param global
+ * The global data.
+ * @param options_force
+ * Force the given process options, only supporting a subset of process options.
+ *
+ * If controller_process_option_asynchronous_d, then asynchronously execute.
+ * If not controller_process_option_asynchronous_d, then synchronously execute.
+ * @param alias_rule
+ * The alias of the rule, such as "boot/init".
+ * @param action
+ * The action to perform based on the action type codes.
+ * @param options
+ * The process options to pass to the process.
+ * @param type
+ * The process type, such as controller_process_type_entry_e.
+ * @param stack
+ * A stack representing the processes already running in this rule process dependency tree.
+ * This is used to prevent circular dependencies.
+ * @param cache
+ * A structure for containing and caching relevant data.
+ *
+ * @return
+ * F_none on success.
+ * F_busy on success and the process was found to already be running (nothing to do).
+ *
+ * F_found_not (with error bit) if unable to for a process for the given rule id.
+ * F_interrupt (with error bit) on receiving a process signal, such as an interrupt signal.
+ * F_recurse (with error bit) on recursion error (the process is already on the process stack).
+ *
+ * Status from: controller_rule_process().
+ *
+ * Errors (with error bit) from: controller_rule_process().
+ * Errors (with error bit) from: f_string_dynamic_append().
+ * Errors (with error bit) from: f_thread_create().
+ *
+ * @see controller_rule_process()
+ * @see f_string_dynamic_append()
+ * @see f_thread_create()
+ */
+#ifndef _di_controller_rule_process_begin_
+ extern f_status_t controller_rule_process_begin(const controller_global_t global, const uint8_t options_force, const f_string_static_t alias_rule, const uint8_t action, const uint8_t options, const uint8_t type, const f_array_lengths_t stack, const controller_cache_t cache) F_attribute_visibility_internal_d;
+#endif // _di_controller_rule_process_begin_
+
+/**
+ * Helper for calling controller_rule_process().
+ *
+ * This does all the preparation work that needs to be synchronously performed within the same thread.
+ * This will copy the rule by the alias to the process structure.
+ *
+ * @param options_force
+ * Force the given process options, only supporting a subset of process options.
+ *
+ * If controller_process_option_asynchronous_d, then asynchronously execute.
+ * If not controller_process_option_asynchronous_d, then synchronously execute.
+ * @param process
+ * The process data.
+ *
+ * @return
+ * F_none on success.
+ * F_found on the process was found to already be running (nothing to do).
+ * F_process_not if the process was not executed because it is a "consider" Action.
+ *
+ * F_found_not (with error bit) if unable to for a process for the given rule id.
+ * F_interrupt (with error bit) on receiving a process signal, such as an interrupt signal.
+ *
+ * Status from: controller_rule_process().
+ *
+ * Errors (with error bit) from: controller_rule_copy().
+ * Errors (with error bit) from: controller_rule_process().
+ *
+ * @see controller_rule_copy()
+ * @see controller_rule_process()
+ * @see controller_rule_process_begin()
+ */
+#ifndef _di_controller_rule_process_do_
+ extern f_status_t controller_rule_process_do(const uint8_t options_force, controller_process_t * const process) F_attribute_visibility_internal_d;
+#endif // _di_controller_rule_process_do_
+
+/**
+ * Read the rule file, extracting all valid items.
+ *
+ * @param global
+ * The global data.
+ * @param is_normal
+ * If TRUE, then process as if this operates during a normal operation (entry and control).
+ * If FALSE, then process as if this operates during a an exit operation.
+ * @param alias
+ * The string identifying the rule.
+ * This is constructed from the path parts to the file without the file extension and without the settings directory prefix.
+ * "/etc/controller/rules/example/my.rule" would have a rule id of "example/my".
+ * @param cache
+ * A structure for containing and caching relevant data.
+ * @param entry
+ * The entry containing the rule being read.
+ * @param rule
+ * The processed rule.
+ * The rule status will be updated by this function.
+ *
+ * @return
+ * F_none on success.
+ *
+ * Simplified status (with error bit) from controller_status_simplify_error() on failure.
+ *
+ * @see controller_rule_items_increase_by().
+ * @see controller_rule_item_read().
+ * @see f_fss_count_lines().
+ * @see fl_fss_apply_delimit().
+ * @see f_string_dynamic_partial_append().
+ * @see f_string_dynamic_partial_append_nulless().
+ * @see f_string_dynamic_terminate_after().
+ * @see fll_fss_basic_list_read().
+ */
+#ifndef _di_controller_rule_read_
+ extern f_status_t controller_rule_read(const controller_global_t global, const bool is_normal, const f_string_static_t alias, controller_cache_t * const cache, controller_entry_t * const entry, controller_rule_t * const rule) F_attribute_visibility_internal_d;
+#endif // _di_controller_rule_read_
+
+/**
+ * Process a number from a rule file, incrementing index as necessary.
+ *
+ * This prints error messages as necessary.
+ *
+ * This is intended to be called by controller_rule_action_read().
+ *
+ * @param global
+ * The global data.
+ * @param name
+ * The name representing the value whose number is being processed.
+ * @param cache
+ * A structure for containing and caching relevant data.
+ * @param index
+ * The position within the content action array for some rule to process.
+ * @param number
+ * The processed number will be saved here.
+ *
+ * @return
+ * F_none on success.
+ *
+ * F_valid_not (with error bit) on failure due to invalid value.
+ *
+ * Errors (with error bit) from: fl_conversion_string_to_number_signed().
+ *
+ * @see controller_rule_action_read()
+ * @see fl_conversion_string_to_number_signed()
+ */
+#ifndef _di_controller_rule_action_read_rerun_number_
+ extern f_status_t controller_rule_action_read_rerun_number(const controller_global_t global, const f_string_t name, controller_cache_t * const cache, f_array_length_t * const index, f_number_unsigned_t * const number) F_attribute_visibility_internal_d;
+#endif // _di_controller_rule_action_read_rerun_number_
+
+/**
+ * Read the content within the buffer, extracting all valid settings.
+ *
+ * This will perform additional FSS read functions as appropriate.
+ *
+ * Errors from this are not considered fatal, but the first error code encountered is returned.
+ * Memory failure errors are always immediately returned.
+ *
+ * @param global
+ * The global data.
+ * @param is_normal
+ * If TRUE, then process as if this operates during a normal operation (entry and control).
+ * If FALSE, then process as if this operates during a an exit operation.
+ * @param setting
+ * The controller settings data.
+ * @param cache
+ * A structure for containing and caching relevant data.
+ * @param rule
+ * The processed rule.
+ *
+ * @return
+ * F_none on success.
+ *
+ * F_valid_not (with error bit) on success but there were one or more invalid settings encountered.
+ *
+ * Errors (with error bit) from: f_string_dynamic_partial_append_nulless().
+ * Errors (with error bit) from: fl_string_dynamic_rip_nulless().
+ * Errors (with error bit) from: f_string_dynamics_increase().
+ * Errors (with error bit) from: f_string_maps_increase().
+ * Errors (with error bit) from: fll_fss_extended_read().
+ * Errors (with error bit) from: fll_path_canonical().
+ *
+ * @see f_string_dynamic_partial_append_nulless()
+ * @see f_string_dynamics_increase()
+ * @see f_string_maps_increase()
+ * @see fl_string_dynamic_rip_nulless()
+ * @see fll_fss_extended_read()
+ * @see fll_path_canonical()
+ */
+#ifndef _di_controller_rule_setting_read_
+ extern f_status_t controller_rule_setting_read(const controller_global_t global, const bool is_normal, const controller_setting_t setting, controller_cache_t * const cache, controller_rule_t * const rule) F_attribute_visibility_internal_d;
+#endif // _di_controller_rule_setting_read_
+
+/**
+ * Perform a simulated execution of the given rule.
+ *
+ * This simply prints information about the rule.
+ *
+ * This automatically sets the rule's status to F_complete.
+ *
+ * @param global
+ * The global data.
+ * @param rule
+ * The rule to process.
+ * @param action
+ * The action to perform based on the action type codes.
+ * @param options
+ * A number using bits to represent specific boolean options.
+ * If no bits set, then operate normally in a synchronous manner.
+ * If bit controller_process_option_simulate_d, then the rule execution is in simulation mode (printing a message that the rule would be executed but does not execute the rule).
+ * If bit controller_process_option_asynchronous_d, then run asynchronously.
+ * @param cache
+ * A structure for containing and caching relevant data.
+ */
+#ifndef _di_controller_rule_validate_
+ extern void controller_rule_validate(const controller_global_t global, const controller_rule_t rule, const uint8_t action, const uint8_t options, controller_cache_t * const cache) F_attribute_visibility_internal_d;
+#endif // _di_controller_rule_validate_
+
+/**
+ * Wait until all currently running Rule processes are complete.
+ *
+ * @param global
+ * The global data.
+ * @param is_normal
+ * If TRUE, then process as if this operates during a normal operation (entry and control).
+ * If FALSE, then process as if this operates during a an exit operation.
+ * This is ignored when caller is not NULL.
+ * @param required
+ * If TRUE, then only process required rules and if a required rule has failed, return.
+ * If FALSE, process all waits, returning normally (required rules still result in failure).
+ * @param caller
+ * The process representing the caller so that the process never waits on itself.
+ * (optional) set to 0 when calling from a thread that is not running/executing any process.
+ * Failure to set this to the process on a thread running/executing a process will likely result in a deadlock.
+ *
+ * @return
+ * F_none on success.
+ * F_data_not on success and nothing to do.
+ * F_require on success, but a required rule has not been run yet.
+ *
+ * F_interrupt (with error bit) on receiving a process signal, such as an interrupt signal.
+ * F_require (with error bit set) if a required process is in failed status when required is TRUE.
+ */
+#ifndef _di_controller_rule_wait_all_
+ extern f_status_t controller_rule_wait_all(const controller_global_t global, const bool is_normal, const bool required, controller_process_t * const caller) F_attribute_visibility_internal_d;
+#endif // _di_controller_rule_wait_all_
+
+/**
+ * Wait until all currently running Rule processes are complete for some process type.
+ *
+ * @param global
+ * The global data.
+ * @param type
+ * The process type to use when checking if thread is enabled.
+ * @param required
+ * If TRUE, then only process required rules and if a required rule has failed, return.
+ * If FALSE, process all waits, returning normally.
+ * @param caller
+ * The process representing the caller so that the process never waits on itself.
+ * (optional) set to 0 when calling from a thread that is not running/executing any process.
+ * Failure to set this to the process on a thread running/executing a process will likely result in a deadlock.
+ *
+ * @return
+ * Success from controller_rule_wait_all().
+ *
+ * Errors (with error bit) from: controller_rule_wait_all().
+ *
+ * @see controller_rule_wait_all()
+ */
+#ifndef _di_controller_rule_wait_all_process_type_
+ extern f_status_t controller_rule_wait_all_process_type(const controller_global_t global, const uint8_t type, const bool required, controller_process_t * const caller) F_attribute_visibility_internal_d;
+#endif // _di_controller_rule_wait_all_process_type_
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // _PRIVATE_rule_h
--- /dev/null
+#include "../controller/controller.h"
+#include "../common/private-common.h"
+#include "../rule/private-rule_print.h"
+#include "../lock/private-lock_print.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef _di_controller_rule_print_error_
+ void controller_rule_print_error(controller_thread_t * const thread, const fl_print_t print, const controller_cache_action_t cache, const f_status_t status, const f_string_t function, const bool fallback, const bool item) {
+
+ if (print.verbosity == f_console_verbosity_quiet_e) return;
+ if (status == F_interrupt) return;
+
+ // fll_error_print() automatically locks, so manually handle only the mutex locking and flushing rather than calling controller_lock_print().
+ f_thread_mutex_lock(&thread->lock.print);
+
+ fll_error_print(print, status, function, fallback);
+
+ flockfile(print.to.stream);
+
+ controller_rule_print_error_cache(print, cache, item);
+
+ controller_unlock_print_flush(print.to, thread);
+ }
+#endif // _di_controller_rule_print_error_
+
+#ifndef _di_controller_rule_print_error_cache_
+ void controller_rule_print_error_cache(const fl_print_t print, const controller_cache_action_t cache, const bool item) {
+
+ if (print.verbosity == f_console_verbosity_quiet_e) return;
+
+ fl_print_format("%c%[%SWhile processing ", print.to.stream, f_string_eol_s[0], print.context, print.prefix);
+
+ if (cache.name_action.used) {
+ fl_print_format("%s '%]", print.to.stream, item ? controller_action_s : controller_value_s, print.context);
+ fl_print_format("%[%Q%]", print.to.stream, print.notable, cache.name_action, print.notable);
+ fl_print_format("%[' on line%] ", print.to.stream, print.context, print.context);
+ fl_print_format("%[%un%]", print.to.stream, print.notable, cache.line_action, print.notable);
+ fl_print_format("%[ for ", print.to.stream, print.context);
+ }
+
+ if (cache.name_item.used) {
+ fl_print_format("rule %s '%]", print.to.stream, item ? controller_item_s : controller_setting_s, print.context);
+ fl_print_format("%[%Q%]", print.to.stream, print.notable, cache.name_item, print.notable);
+ fl_print_format("%[' on line%] ", print.to.stream, print.context, print.context);
+ fl_print_format("%[%un%]", print.to.stream, print.notable, cache.line_item, print.notable);
+ fl_print_format("%[ for ", print.to.stream, print.context);
+ }
+
+ if (cache.name_file.used) {
+ fl_print_format("rule file '%]%[%Q%]%['", print.to.stream, print.context, print.notable, cache.name_file, print.notable, print.context);
+ }
+
+ fl_print_format(".%]%c", print.to.stream, print.context, f_string_eol_s[0]);
+ }
+#endif // _di_controller_rule_print_error_cache_
+
+#ifndef _di_controller_rule_item_print_error_
+ void controller_rule_item_print_error(controller_thread_t * const thread, const fl_print_t print, const controller_cache_action_t cache, const bool item, const f_status_t status) {
+
+ if (print.verbosity == f_console_verbosity_quiet_e) return;
+ if (status == F_interrupt) return;
+
+ // fll_error_print() automatically locks, so manually handle only the mutex locking and flushing rather than calling controller_lock_print().
+ f_thread_mutex_lock(&thread->lock.print);
+
+ controller_rule_print_error_cache(print, cache, item);
+
+ flockfile(print.to.stream);
+
+ controller_unlock_print_flush(print.to, thread);
+ }
+#endif // _di_controller_rule_item_print_error_
+
+#ifndef _di_controller_rule_item_print_error_execute_
+ void controller_rule_item_print_error_execute(const bool script_is, const f_string_t name, const f_status_t status, controller_process_t * const process) {
+
+ if (((controller_main_t *) process->main_data)->error.verbosity != f_console_verbosity_quiet_e) {
+ fl_print_t * const print = &((controller_main_t *) process->main_data)->error;
+
+ controller_lock_print(print->to, (controller_thread_t *) process->main_thread);
+
+ fl_print_format("%c%[%SThe %s '%]", print->to.stream, f_string_eol_s[0], print->context, print->prefix, script_is ? controller_script_s : controller_program_s, print->context);
+ fl_print_format("%[%S%]", print->to.stream, print->notable, name, print->notable);
+
+ if (status == F_control_group || status == F_limit || status == F_processor || status == F_schedule) {
+ fl_print_format("%[' failed due to a failure to setup the '%]%[", print->to.stream, print->context, print->context, print->notable);
+
+ if (status == F_control_group) {
+ f_print_terminated(controller_cgroup_s, print->to.stream);
+ }
+ else if (status == F_limit) {
+ f_print_terminated(controller_limit_s, print->to.stream);
+ }
+ else if (status == F_processor) {
+ f_print_terminated(controller_processor_s, print->to.stream);
+ }
+ else if (status == F_schedule) {
+ f_print_terminated(controller_scheduler_s, print->to.stream);
+ }
+
+ fl_print_format("%]%['.%]%c", print->to.stream, print->notable, print->context, print->context, f_string_eol_s[0]);
+ }
+ else if (WIFEXITED(process->result) ? WEXITSTATUS(process->result) : 0) {
+ const uint8_t code = WIFEXITED(process->result) ? WEXITSTATUS(process->result) : 0;
+
+ if (code == F_execute_access) {
+ fl_print_format("%[' access denied.%]%c", print->to.stream, print->context, print->context, f_string_eol_s[0]);
+ }
+ else if (code == F_execute_bad) {
+ fl_print_format("%[' cannot execute, unsupported format.%]%c", print->to.stream, print->context, print->context, f_string_eol_s[0]);
+ }
+ else if (code == F_execute_buffer) {
+ fl_print_format("%[' invalid memory access in arguments buffer.%]%c", print->to.stream, print->context, print->context, f_string_eol_s[0]);
+ }
+ else if (code == F_execute_busy) {
+ fl_print_format("%[' required resources are unavailable, too busy.%]%c", print->to.stream, print->context, print->context, f_string_eol_s[0]);
+ }
+ else if (code == F_execute_capability) {
+ fl_print_format("%[' failed to setup capabilities.%]%c", print->to.stream, print->context, print->context, f_string_eol_s[0]);
+ }
+ else if (code == F_execute_control_group) {
+ fl_print_format("%[' failed to setup control group.%]%c", print->to.stream, print->context, print->context, f_string_eol_s[0]);
+ }
+ else if (code == F_execute_child) {
+ fl_print_format("%[' failed to setup child process.%]%c", print->to.stream, print->context, print->context, f_string_eol_s[0]);
+ }
+ else if (code == F_execute_directory_not) {
+ fl_print_format("%[' invalid path, part of the path is not a valid directory.%]%c", print->to.stream, print->context, print->context, f_string_eol_s[0]);
+ }
+ else if (code == F_execute_failure) {
+ fl_print_format("%[' failed during execution.%]%c", print->to.stream, print->context, print->context, f_string_eol_s[0]);
+ }
+ else if (code == F_execute_file_found_not) {
+ fl_print_format("%[' could not be executed, unable to find file.%]%c", print->to.stream, print->context, print->context, f_string_eol_s[0]);
+ }
+ else if (code == F_execute_file_type_directory) {
+ fl_print_format("%[' ELF interpreter is a directory.%]%c", print->to.stream, print->context, print->context, f_string_eol_s[0]);
+ }
+ else if (code == F_execute_fork_not) {
+ fl_print_format("%[' fork failure.%]%c", print->to.stream, print->context, print->context, f_string_eol_s[0]);
+ }
+ else if (code == F_execute_format_not) {
+ fl_print_format("%[' could not be executed because the program has an invalid ELF header.%]%c", print->to.stream, print->context, print->context, f_string_eol_s[0]);
+ }
+ else if (code == F_execute_group) {
+ fl_print_format("%[' failed to setup group.%]%c", print->to.stream, print->context, print->context, f_string_eol_s[0]);
+ }
+ else if (code == F_execute_input_output) {
+ fl_print_format("%[' I/O failure.%]%c", print->to.stream, print->context, print->context, f_string_eol_s[0]);
+ }
+ else if (code == F_execute_limit) {
+ fl_print_format("%[' failed to setup resource limits.%]%c", print->to.stream, print->context, print->context, f_string_eol_s[0]);
+ }
+ else if (code == F_execute_loop) {
+ fl_print_format("%[' max recursion reached.%]%c", print->to.stream, print->context, print->context, f_string_eol_s[0]);
+ }
+ else if (code == F_execute_memory_not) {
+ fl_print_format("%[' out of memory.%]%c", print->to.stream, print->context, print->context, f_string_eol_s[0]);
+ }
+ else if (code == F_execute_name_not) {
+ fl_print_format("%[' file name or path is too long.%]%c", print->to.stream, print->context, print->context, f_string_eol_s[0]);
+ }
+ else if (code == F_execute_nice) {
+ fl_print_format("%[' failed to setup niceness.%]%c", print->to.stream, print->context, print->context, f_string_eol_s[0]);
+ }
+ else if (code == F_execute_parameter) {
+ fl_print_format("%[' invalid parameter.%]%c", print->to.stream, print->context, print->context, f_string_eol_s[0]);
+ }
+ else if (code == F_execute_pipe) {
+ fl_print_format("%[' pipe failed.%]%c", print->to.stream, print->context, print->context, f_string_eol_s[0]);
+ }
+ else if (code == F_execute_processor) {
+ fl_print_format("%[' failed to setup processor affinity.%]%c", print->to.stream, print->context, print->context, f_string_eol_s[0]);
+ }
+ else if (code == F_execute_prohibited) {
+ fl_print_format("%[' access prohibited.%]%c", print->to.stream, print->context, print->context, f_string_eol_s[0]);
+ }
+ else if (code == F_execute_resource_not) {
+ fl_print_format("%[' resource limit reached.%]%c", print->to.stream, print->context, print->context, f_string_eol_s[0]);
+ }
+ else if (code == F_execute_schedule) {
+ fl_print_format("%[' failed to setup scheduler.%]%c", print->to.stream, print->context, print->context, f_string_eol_s[0]);
+ }
+ else if (code == F_execute_terminal) {
+ fl_print_format("%[' failed while processing the terminal.%]%c", print->to.stream, print->context, print->context, f_string_eol_s[0]);
+ }
+ else if (code == F_execute_terminal_known_not) {
+ fl_print_format("%[' cannot process terminal, unknown terminal control command.%]%c", print->to.stream, print->context, print->context, f_string_eol_s[0]);
+ }
+ else if (code == F_execute_terminal_not) {
+ fl_print_format("%[' cannot process terminal, not a known terminal.%]%c", print->to.stream, print->context, print->context, f_string_eol_s[0]);
+ }
+ else if (code == F_execute_terminal_prohibited) {
+ fl_print_format("%[' insufficient permissions to process the terminal.%]%c", print->to.stream, print->context, print->context, f_string_eol_s[0]);
+ }
+ else if (code == F_execute_terminal_valid_not) {
+ fl_print_format("%[' invalid parameter while processing the terminal.%]%c", print->to.stream, print->context, print->context, f_string_eol_s[0]);
+ }
+ else if (code == F_execute_too_large) {
+ fl_print_format("%[' too many arguments or arguments are too large.%]%c", print->to.stream, print->context, print->context, f_string_eol_s[0]);
+ }
+ else if (code == F_execute_user) {
+ fl_print_format("%[' failed to setup user.%]%c", print->to.stream, print->context, print->context, f_string_eol_s[0]);
+ }
+ else if (code == F_execute_valid_not) {
+ fl_print_format("%[' unknown ELF interpreter format.%]%c", print->to.stream, print->context, print->context, f_string_eol_s[0]);
+ }
+ else {
+ fl_print_format("%[' failed with the execute error code %]", print->to.stream, print->context, print->context);
+ fl_print_format("%[%i%]", print->to.stream, print->notable, code, print->notable);
+ fl_print_format("%[.%]%c", print->to.stream, print->context, print->context, f_string_eol_s[0]);
+ }
+ }
+ else {
+ fl_print_format("%[' failed.%]%c", print->to.stream, print->context, print->context, f_string_eol_s[0]);
+ }
+
+ controller_unlock_print_flush(print->to, (controller_thread_t *) process->main_thread);
+ }
+ }
+#endif // _di_controller_rule_item_print_error_execute_
+
+#ifndef _di_controller_rule_action_print_error_missing_pid_
+ void controller_rule_action_print_error_missing_pid(const fl_print_t print, const f_string_t alias) {
+
+ if (print.verbosity == f_console_verbosity_quiet_e) return;
+
+ fl_print_format("%c%[%SThe rule '%]", print.to.stream, f_string_eol_s[0], print.context, print.prefix, print.context);
+ fl_print_format("%[%S%]", print.to.stream, print.notable, alias, print.notable);
+ fl_print_format("%[' is not designating a pid file.%]%c", print.to.stream, print.context, print.context, f_string_eol_s[0]);
+ }
+#endif // _di_controller_rule_action_print_error_missing_pid_
+
+#ifndef _di_controller_rule_item_print_error_need_want_wish_
+ void controller_rule_item_print_error_need_want_wish(const fl_print_t print, const f_string_t need_want_wish, const f_string_t value, const f_string_t why) {
+
+ if (print.verbosity == f_console_verbosity_quiet_e) return;
+
+ fl_print_format("%c%[%SThe %s rule '%]", print.to.stream, f_string_eol_s[0], print.context, print.prefix, need_want_wish, print.context);
+ fl_print_format("%[%S%]", print.to.stream, print.notable, value, print.notable);
+ fl_print_format("%[' %S.%]%c", print.to.stream, print.context, why, print.context, f_string_eol_s[0]);
+ }
+#endif // _di_controller_rule_item_print_error_need_want_wish_
+
+#ifndef _di_controller_rule_item_print_error_rule_not_loaded_
+ void controller_rule_item_print_error_rule_not_loaded(const fl_print_t print, const f_string_t alias) {
+
+ if (print.verbosity == f_console_verbosity_quiet_e) return;
+
+ fl_print_format("%c%[%SThe rule '%]", print.to.stream, f_string_eol_s[0], print.context, print.prefix, print.context);
+ fl_print_format("%[%S%]", print.to.stream, print.notable, alias, print.notable);
+ fl_print_format("%[' is no longer loaded.%]%c", print.to.stream, print.context, print.context, f_string_eol_s[0]);
+ }
+#endif // _di_controller_rule_item_print_error_rule_not_loaded_
+
+#ifndef _di_controller_rule_setting_read_print_error_
+ void controller_rule_setting_read_print_error(const fl_print_t print, const f_string_t message, const f_array_length_t index, const f_array_length_t line_item, controller_thread_t * const thread, controller_cache_t * const cache) {
+
+ if (print.verbosity == f_console_verbosity_quiet_e) return;
+
+ // Get the current line number within the settings item.
+ cache->action.line_item = line_item;
+ f_fss_count_lines(cache->buffer_item, cache->object_actions.array[index].start, &cache->action.line_item);
+
+ cache->action.line_action = ++cache->action.line_item;
+
+ controller_lock_print(print.to, thread);
+
+ fl_print_format("%c%[%SRule setting %S.%]%c", print.to.stream, f_string_eol_s[0], print.context, print.prefix, message, print.context, f_string_eol_s[0]);
+
+ controller_rule_print_error_cache(print, cache->action, F_false);
+
+ controller_unlock_print_flush(print.to, thread);
+ }
+#endif // _di_controller_rule_setting_read_print_error_
+
+#ifndef _di_controller_rule_setting_read_print_error_with_range_
+ void controller_rule_setting_read_print_error_with_range(const fl_print_t print, const f_string_t before, const f_string_range_t range, const f_string_t after, const f_array_length_t index, const f_array_length_t line_item, controller_thread_t * const thread, controller_cache_t * const cache) {
+
+ if (print.verbosity == f_console_verbosity_quiet_e) return;
+
+ // Get the current line number within the settings item.
+ cache->action.line_item = line_item;
+ f_fss_count_lines(cache->buffer_item, cache->object_actions.array[index].start, &cache->action.line_item);
+
+ cache->action.line_action = ++cache->action.line_item;
+
+ controller_lock_print(print.to, thread);
+
+ fl_print_format("%c%[%SRule setting%S '%]", print.to.stream, f_string_eol_s[0], print.context, print.prefix, before, print.context);
+ fl_print_format("%[%/Q%]", print.to.stream, print.notable, cache->buffer_item, range, print.notable);
+ fl_print_format("%['%S.%]%c", print.to.stream, print.context, after, print.context, f_string_eol_s[0]);
+
+ controller_rule_print_error_cache(print, cache->action, F_false);
+
+ controller_unlock_print_flush(print.to, thread);
+ }
+#endif // _di_controller_rule_setting_read_print_error_with_range_
+
+#ifndef _di_controller_rule_setting_read_print_value_
+ void controller_rule_setting_read_print_value(const controller_global_t global, const f_string_t name, const f_string_t name_sub, const f_string_static_t value, const f_string_t suffix) {
+
+ if (global.main->error.verbosity != f_console_verbosity_debug_e && !(global.main->error.verbosity == f_console_verbosity_verbose_e && global.main->parameters[controller_parameter_simulate_e].result == f_console_result_found_e)) {
+ return;
+ }
+
+ controller_lock_print(global.main->output.to, global.thread);
+
+ fl_print_format("%cProcessing rule item action '%[%S%]' setting ", global.main->output.to.stream, f_string_eol_s[0], global.main->context.set.title, name, global.main->context.set.title);
+
+ if (name_sub) {
+ fl_print_format("'%[%S%]'", global.main->output.to.stream, global.main->context.set.notable, name_sub, global.main->context.set.notable);
+ }
+ else {
+ f_print_terminated("value", global.main->output.to.stream);
+ }
+
+ fl_print_format(" to '%[%Q%]'", global.main->output.to.stream, global.main->context.set.important, value, global.main->context.set.important);
+ fl_print_format("%S.%c", global.main->output.to.stream, suffix, f_string_eol_s[0]);
+
+ controller_unlock_print_flush(global.main->output.to, global.thread);
+ }
+#endif // _di_controller_rule_setting_read_print_value_
+
+#ifndef _di_controller_rule_setting_read_print_values_
+ void controller_rule_setting_read_print_values(const controller_global_t global, const f_string_t name, const f_array_length_t index, controller_cache_t * const cache) {
+
+ if (global.main->error.verbosity != f_console_verbosity_debug_e && !(global.main->error.verbosity == f_console_verbosity_verbose_e && global.main->parameters[controller_parameter_simulate_e].result == f_console_result_found_e)) {
+ return;
+ }
+
+ controller_lock_print(global.main->output.to, global.thread);
+
+ fl_print_format("%cProcessing rule item action '%[%S%]' setting value to", global.main->output.to.stream, f_string_eol_s[0], global.main->context.set.title, name, global.main->context.set.title);
+
+ for (f_array_length_t j = 0; j < cache->content_actions.array[index].used; ++j) {
+
+ fl_print_format(" '%[%/Q%]'", global.main->output.to.stream, global.main->context.set.important, cache->buffer_item, cache->content_actions.array[index].array[j], global.main->context.set.important);
+
+ if (j + 2 == cache->content_actions.array[index].used) {
+ if (cache->content_actions.array[index].used > 2) {
+ f_print_terminated(",", global.main->output.to.stream);
+ }
+
+ f_print_terminated(" and", global.main->output.to.stream);
+ }
+ else if (j + 1 < cache->content_actions.array[index].used) {
+ f_print_terminated(",", global.main->output.to.stream);
+ }
+ } // for
+
+ fl_print_format(".%c", global.main->output.to.stream, f_string_eol_s[0]);
+
+ controller_unlock_print_flush(global.main->output.to, global.thread);
+ }
+#endif // _di_controller_rule_setting_read_print_value_
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
--- /dev/null
+/**
+ * FLL - Level 3
+ *
+ * Project: Controller
+ * API Version: 0.5
+ * Licenses: lgpl-2.1-or-later
+ */
+#ifndef _PRIVATE_rule_print_h
+#define _PRIVATE_rule_print_h
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Print generic error/warning information.
+ *
+ * This is essentially a wrapper to fll_error_print() that includes locking.
+ *
+ * @param thread
+ * The thread data.
+ * @param print
+ * Designates how printing is to be performed.
+ * @param cache
+ * The action cache.
+ * @param status
+ * The status code to process.
+ * Make sure this has F_status_set_fine() called if the status code has any error or warning bits.
+ * @param function
+ * The name of the function where the error happened.
+ * Set to 0 to disable.
+ * @param fallback
+ * Set to F_true to print the fallback error message for unknown errors.
+ * @param item
+ * If TRUE, then this error is associated with an item.
+ * If FALSE, then this error is associated with a rule setting.
+ *
+ * @see fll_error_print()
+ * @see controller_rule_print_error_cache()
+ */
+#ifndef _di_controller_rule_print_error_
+ extern void controller_rule_print_error(controller_thread_t * const thread, const fl_print_t print, const controller_cache_action_t cache, const f_status_t status, const f_string_t function, const bool fallback, const bool item) F_attribute_visibility_internal_d;
+#endif // _di_controller_rule_print_error_
+
+/**
+ * Print additional error/warning information in addition to existing error.
+ *
+ * This is explicitly intended to be used in addition to the error message.
+ *
+ * This neither locks the thread nor does it check to see if output is enabled or disabled.
+ *
+ * @param print
+ * The error or warning output structure.
+ * @param cache
+ * A structure for containing and caching relevant data.
+ * @param item
+ * If TRUE, then this error is associated with an item.
+ * If FALSE, then this error is associated with a rule setting.
+ *
+ * @see controller_rule_action_read()
+ * @see controller_rule_item_read()
+ * @see controller_rule_items_read()
+ * @see controller_rule_read()
+ * @see controller_rule_setting_read()
+ */
+#ifndef _di_controller_rule_print_error_cache_
+ extern void controller_rule_print_error_cache(const fl_print_t print, const controller_cache_action_t cache, const bool item) F_attribute_visibility_internal_d;
+#endif // _di_controller_rule_print_error_cache_
+
+/**
+ * Print additional error/warning information in addition to existing error.
+ *
+ * This is explicitly intended to be used in addition to the error message.
+ *
+ * @param thread
+ * The thread data.
+ * @param print
+ * The error or warning print structure.
+ * @param cache
+ * A structure for containing and caching relevant data.
+ * @param item
+ * If TRUE, then this error is associated with an item.
+ * If FALSE, then this error is associated with a rule setting.
+ * @param status
+ * The status code representing the failure (without the error bit set).
+ *
+ * @see controller_rule_print_error_cache()
+ */
+#ifndef _di_controller_rule_item_print_error_
+ extern void controller_rule_item_print_error(controller_thread_t * const thread, const fl_print_t print, const controller_cache_action_t cache, const bool item, const f_status_t status) F_attribute_visibility_internal_d;
+#endif // _di_controller_rule_item_print_error_
+
+/**
+ * Print an error or warning message related to the failed execution of some program or script.
+ *
+ * @param script_is
+ * If TRUE, then this represents a script.
+ * If FALSE, then this represents a program.
+ * @param name
+ * The name of the program or script.
+ * @param code
+ * The code returned by the executed program or script.
+ * @param status
+ * The status code representing the failure (without the error bit set).
+ * @param process
+ * The process to use.
+ */
+#ifndef _di_controller_rule_item_print_error_execute_
+ extern void controller_rule_item_print_error_execute(const bool script_is, const f_string_t name, const f_status_t status, controller_process_t * const process) F_attribute_visibility_internal_d;
+#endif // _di_controller_rule_item_print_error_execute_
+
+/**
+ * Print an error or warning message about some rule not having the pid file information.
+ *
+ * @param print
+ * The error or warning output structure.
+ * @param alias
+ * The rule alias of the rule that is missing the pid file designation.
+ */
+#ifndef _di_controller_rule_action_print_error_missing_pid_
+ extern void controller_rule_action_print_error_missing_pid(const fl_print_t print, const f_string_t alias) F_attribute_visibility_internal_d;
+#endif // _di_controller_rule_action_print_error_missing_pid_
+
+/**
+ * Print an error or warning message related to need/want/wish settings of some rule.
+ *
+ * @param print
+ * The error or warning output structure.
+ * @param need_want_wish
+ * The appropriate string, such as "needs", "wants", or "wishes for" to output when describing this error/warning.
+ * This string is expected to already be "safe" (no control characters, etc..).
+ * @param value
+ * The value that is the error or warning.
+ * @param why
+ * A short explanation on why this is an error or warning.
+ */
+#ifndef _di_controller_rule_item_print_error_need_want_wish_
+ extern void controller_rule_item_print_error_need_want_wish(const fl_print_t print, const f_string_t need_want_wish, const f_string_t value, const f_string_t why) F_attribute_visibility_internal_d;
+#endif // _di_controller_rule_item_print_error_need_want_wish_
+
+/**
+ * Print an error or warning message about some rule not being loaded.
+ *
+ * @param print
+ * The error or warning output structure.
+ * @param alias
+ * The rule alias of the rule that is not loaded.
+ */
+#ifndef _di_controller_rule_item_print_error_rule_not_loaded_
+ extern void controller_rule_item_print_error_rule_not_loaded(const fl_print_t print, const f_string_t alias) F_attribute_visibility_internal_d;
+#endif // _di_controller_rule_item_print_error_rule_not_loaded_
+
+/**
+ * Print a message about a rule setting problem.
+ *
+ * This is intended to be explicitly called by controller_rule_setting_read().
+ * This is intended only to be used for simple messages.
+ *
+ * @param print
+ * The error or warning output structure.
+ * @param message
+ * The string to append to the message being printed.
+ * @param index
+ * The position in the object actions cache representing the object.
+ * @param line_item
+ * The current line number.
+ * @param thread
+ * The thread data.
+ * @param cache
+ * A structure for containing and caching relevant data.
+ *
+ * @see controller_rule_setting_read()
+ */
+#ifndef _di_controller_rule_setting_read_print_error_
+ extern void controller_rule_setting_read_print_error(const fl_print_t print, const f_string_t message, const f_array_length_t index, const f_array_length_t line_item, controller_thread_t * const thread, controller_cache_t * const cache) F_attribute_visibility_internal_d;
+#endif // _di_controller_rule_setting_read_print_error_
+
+/**
+ * Print a message about a rule setting problem, with additional messages about the value.
+ *
+ * This is intended to be explicitly called by controller_rule_setting_read().
+ * This is intended only to be used for simple messages.
+ *
+ * @param print
+ * The error or warning output structure.
+ * @param before
+ * The string to add to the message being printed (before the value).
+ * @param range
+ * The range within the cache item buffer representing the value.
+ * @param after
+ * The string to add to the message being printed (after the value).
+ * @param index
+ * The position in the object actions cache representing the object.
+ * @param line_item
+ * The current line number.
+ * @param thread
+ * The thread data.
+ * @param cache
+ * A structure for containing and caching relevant data.
+ *
+ * @see controller_rule_setting_read()
+ */
+#ifndef _di_controller_rule_setting_read_print_error_with_range_
+ extern void controller_rule_setting_read_print_error_with_range(const fl_print_t print, const f_string_t before, const f_string_range_t range, const f_string_t after, const f_array_length_t index, const f_array_length_t line_item, controller_thread_t * const thread, controller_cache_t * const cache) F_attribute_visibility_internal_d;
+#endif // _di_controller_rule_setting_read_print_error_with_range_
+
+/**
+ * Print message regarding the population of a setting when in simulation or verbose mode.
+ *
+ * @param global
+ * The global data.
+ * @param name
+ * The Object name of the setting being populated.
+ * @param name_sub
+ * (optional) A sub-name associated with the setting being populated.
+ * Set to NULL to disable.
+ * @param value
+ * The value being set.
+ * @param suffix
+ * An additional message to append at the end (before the final period).
+ */
+#ifndef _di_controller_rule_setting_read_print_value_
+ extern void controller_rule_setting_read_print_value(const controller_global_t global, const f_string_t name, const f_string_t name_sub, const f_string_static_t value, const f_string_t suffix) F_attribute_visibility_internal_d;
+#endif // _di_controller_rule_setting_read_print_value_
+
+/**
+ * Print message regarding the population of a setting when in simulation or verbose mode.
+ *
+ * This handles the case where there are multiple values stored in the buffer_item at a given content_actions position.
+ *
+ * @param global
+ * The global data.
+ * @param name
+ * The Object name of the setting being populated.
+ * @param index
+ * Position within the content_actions range cache array.
+ * @param cache
+ * A structure for containing and caching relevant data.
+ */
+#ifndef _di_controller_rule_setting_read_print_values_
+ extern void controller_rule_setting_read_print_values(const controller_global_t global, const f_string_t name, const f_array_length_t index, controller_cache_t * const cache) F_attribute_visibility_internal_d;
+#endif // _di_controller_rule_setting_read_print_values_
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // _PRIVATE_rule_print_h
--- /dev/null
+#include "../controller/controller.h"
+#include "../common/private-common.h"
+#include "../task/private-task.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
--- /dev/null
+/**
+ * FLL - Level 3
+ *
+ * Project: Controller
+ * API Version: 0.5
+ * Licenses: lgpl-2.1-or-later
+ */
+#ifndef _PRIVATE_task_h
+#define _PRIVATE_task_h
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // _PRIVATE_task_h
--- /dev/null
+#include "../controller/controller.h"
+#include "../common/private-common.h"
+#include "../task/private-task.h"
+#include "../task/private-task_print.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
--- /dev/null
+/**
+ * FLL - Level 3
+ *
+ * Project: Controller
+ * API Version: 0.5
+ * Licenses: lgpl-2.1-or-later
+ */
+#ifndef _PRIVATE_task_print_h
+#define _PRIVATE_task_print_h
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // _PRIVATE_task_print_h
--- /dev/null
+#include "../controller/controller.h"
+#include "../common/private-common.h"
+#include "../controller/private-controller.h"
+#include "../controller/private-controller_print.h"
+#include "../lock/private-lock.h"
+#include "../lock/private-lock_print.h"
+#include "../rule/private-rule.h"
+#include "../thread/private-thread.h"
+#include "../thread/private-thread_control.h"
+#include "../thread/private-thread_entry.h"
+#include "../thread/private-thread_process.h"
+#include "../thread/private-thread_rule.h"
+#include "../thread/private-thread_signal.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef _di_controller_thread_cleanup_
+ void * controller_thread_cleanup(void * const arguments) {
+
+ f_thread_cancel_state_set(PTHREAD_CANCEL_DEFERRED, 0);
+
+ const controller_global_t *global = (controller_global_t *) arguments;
+
+ if (global->thread->enabled != controller_thread_enabled_e) return 0;
+
+ const struct timespec delay = controller_time_seconds(global->main->parameters[controller_parameter_simulate_e].result == f_console_result_found_e ? controller_thread_cleanup_interval_short_d : controller_thread_cleanup_interval_long_d);
+
+ f_status_t status = F_none;
+
+ while (global->thread->enabled == controller_thread_enabled_e) {
+
+ nanosleep(&delay, 0);
+
+ if (global->thread->enabled != controller_thread_enabled_e) break;
+
+ if (f_thread_lock_write_try(&global->thread->lock.process) == F_none) {
+ controller_process_t *process = 0;
+
+ f_array_length_t i = 0;
+
+ for (; i < global->thread->processs.size && global->thread->enabled == controller_thread_enabled_e; ++i) {
+
+ if (!global->thread->processs.array[i]) continue;
+
+ process = global->thread->processs.array[i];
+
+ // If "active" has a read lock, then do not attempt to clean it.
+ if (f_thread_lock_write_try(&process->active) != F_none) {
+ continue;
+ }
+
+ // If "lock" has a read or write lock, then do not attempt to clean it.
+ if (f_thread_lock_write_try(&process->lock) != F_none) {
+ f_thread_unlock(&process->active);
+
+ continue;
+ }
+
+ // If process is active or busy, then do not attempt to clean it.
+ if (process->state == controller_process_state_active_e || process->state == controller_process_state_busy_e) {
+ f_thread_unlock(&process->active);
+ f_thread_unlock(&process->lock);
+
+ continue;
+ }
+
+ // If process has a PID file, then it is running in the background, only cleanup if the PID file no longer exists.
+ if (process->path_pids.used) {
+ f_array_length_t j = 0;
+
+ for (; j < process->path_pids.used; ++j) {
+
+ if (process->path_pids.array[j].used && f_file_exists(process->path_pids.array[j].string) == F_true) {
+ break;
+ }
+ } // for
+
+ if (j < process->path_pids.used) {
+ f_thread_unlock(&process->active);
+ f_thread_unlock(&process->lock);
+
+ continue;
+ }
+ }
+
+ f_thread_unlock(&process->lock);
+
+ // Close any still open thread.
+ if (process->id_thread) {
+ status = f_thread_join(process->id_thread, 0);
+
+ if (F_status_is_error_not(status) || F_status_set_fine(status) == F_found_not) {
+ status = f_thread_lock_write(&process->lock);
+
+ if (F_status_is_error(status)) {
+ controller_lock_print_error_critical(global->main->error, F_status_set_fine(status), F_false, global->thread);
+
+ f_thread_unlock(&process->active);
+ continue;
+ }
+
+ process->state = controller_process_state_idle_e;
+ process->id_thread = 0;
+
+ f_thread_mutex_lock(&process->wait_lock);
+ f_thread_condition_signal_all(&process->wait);
+ f_thread_mutex_unlock(&process->wait_lock);
+
+ f_thread_unlock(&process->lock);
+ }
+ else {
+ f_thread_unlock(&process->active);
+
+ continue;
+ }
+ }
+
+ // Deallocate dynamic portions of the structure that are only ever needed while the process is running.
+ controller_cache_delete_simple(&process->cache);
+ f_type_array_lengths_resize(0, &process->stack);
+
+ // Shrink the childs array.
+ if (process->childs.used) {
+ for (; process->childs.used; --process->childs.used) {
+ if (process->childs.array[process->childs.used]) break;
+ } // for
+
+ if (process->childs.used < process->childs.size) {
+ controller_pids_resize(process->childs.used, &process->childs);
+ }
+ }
+
+ // Deallocate the PID files.
+ if (process->path_pids.used) {
+ process->path_pids.used = 0;
+ f_string_dynamics_resize(0, &process->path_pids);
+ }
+
+ // Deallocate any rules in the space that is declared to be unused.
+ if (i >= global->thread->processs.used) {
+ controller_rule_delete_simple(&process->rule);
+ }
+
+ f_thread_unlock(&process->active);
+ } // for
+
+ f_thread_unlock(&global->thread->lock.process);
+ }
+ } // while
+
+ return 0;
+ }
+#endif // _di_controller_thread_cleanup_
+
+#ifndef _di_controller_thread_is_enabled_
+ f_status_t controller_thread_is_enabled(const bool is_normal, controller_thread_t * const thread) {
+
+ if (is_normal) {
+ return thread->enabled == controller_thread_enabled_e;
+ }
+
+ return thread->enabled;
+ }
+#endif // _di_controller_thread_is_enabled_
+
+#ifndef _di_controller_thread_is_enabled_process_
+ f_status_t controller_thread_is_enabled_process(controller_process_t * const process, controller_thread_t * const thread) {
+
+ return controller_thread_is_enabled_process_type(process->type, thread);
+ }
+#endif // _di_controller_thread_is_enabled_process_
+
+#ifndef _di_controller_thread_is_enabled_process_type_
+ f_status_t controller_thread_is_enabled_process_type(const uint8_t type, controller_thread_t * const thread) {
+
+ return controller_thread_is_enabled(type != controller_process_type_exit_e, thread);
+ }
+#endif // _di_controller_thread_is_enabled_process_type_
+
+#ifndef _di_controller_thread_main_
+ f_status_t controller_thread_main(controller_main_t * const main, controller_setting_t * const setting) {
+
+ f_status_t status = F_none;
+
+ controller_thread_t thread = controller_thread_t_initialize;
+ controller_global_t global = macro_controller_global_t_initialize(main, setting, &thread);
+
+ // The global locks must be initialized, but only once, so initialize immediately upon allocation.
+ status = controller_lock_create(&thread.lock);
+
+ if (F_status_is_error(status)) {
+ if (main->error.verbosity != f_console_verbosity_quiet_e) {
+ fll_error_print(main->error, status, "controller_lock_create", F_true);
+ }
+ }
+ else {
+ status = controller_processs_increase(&thread.processs);
+
+ if (F_status_is_error(status)) {
+ controller_print_error(&thread, main->error, F_status_set_fine(status), "controller_processs_increase", F_true);
+ }
+ }
+
+ if (F_status_is_error_not(status)) {
+ status = f_thread_create(0, &thread.id_signal, &controller_thread_signal_normal, (void *) &global);
+ }
+
+ if (F_status_is_error(status)) {
+ thread.id_signal = 0;
+
+ if (main->error.verbosity != f_console_verbosity_quiet_e) {
+ controller_print_error(&thread, main->error, F_status_set_fine(status), "f_thread_create", F_true);
+ }
+ }
+ else {
+ if (main->parameters[controller_parameter_daemon_e].result == f_console_result_found_e) {
+ setting->ready = controller_setting_ready_done_e;
+
+ if (f_file_exists(setting->path_pid.string) == F_true) {
+ if (main->error.verbosity != f_console_verbosity_quiet_e) {
+ controller_lock_print(main->error.to, &thread);
+
+ fl_print_format("%c%[%SThe pid file '%]", main->error.to.stream, f_string_eol_s[0], main->error.context, main->error.prefix ? main->error.prefix : f_string_empty_s, main->error.context);
+ fl_print_format("%[%S%]", main->error.to.stream, main->error.notable, setting->path_pid.string, main->error.notable);
+ fl_print_format("%[' must not already exist.%]%c", main->error.to.stream, main->error.context, main->error.context, f_string_eol_s[0]);
+
+ controller_unlock_print_flush(main->error.to, &thread);
+ }
+
+ setting->ready = controller_setting_ready_abort_e;
+ status = F_status_set_error(F_available_not);
+ }
+ }
+ else if (global.setting->name_entry.used) {
+ const controller_main_entry_t entry = macro_controller_main_entry_t_initialize(&global, global.setting);
+
+ status = f_thread_create(0, &thread.id_entry, &controller_thread_entry, (void *) &entry);
+
+ if (F_status_is_error(status)) {
+ if (main->error.verbosity != f_console_verbosity_quiet_e) {
+ controller_print_error(&thread, main->error, F_status_set_fine(status), "f_thread_create", F_true);
+ }
+ }
+ else {
+ controller_thread_join(&thread.id_entry);
+
+ status = thread.status;
+ thread.id_entry = 0;
+ }
+ }
+ }
+
+ // Only make the rule and control threads available once any/all pre-processing and are completed.
+ if (F_status_is_error_not(status) && status != F_failure && status != F_child && thread.enabled == controller_thread_enabled_e) {
+ if (main->parameters[controller_parameter_validate_e].result == f_console_result_none_e) {
+
+ // Wait for the entry thread to complete before starting the rule thread.
+ controller_thread_join(&thread.id_rule);
+
+ if (thread.enabled && setting->mode == controller_setting_mode_service_e) {
+ status = f_thread_create(0, &thread.id_rule, &controller_thread_rule, (void *) &global);
+
+ if (F_status_is_error(status)) {
+ thread.id_rule = 0;
+ }
+ else {
+ status = f_thread_create(0, &thread.id_cleanup, &controller_thread_cleanup, (void *) &global);
+ }
+
+ if (F_status_is_error(status)) {
+ thread.id_cleanup = 0;
+
+ if (main->error.verbosity != f_console_verbosity_quiet_e) {
+ controller_print_error(&thread, main->error, F_status_set_fine(status), "f_thread_create", F_true);
+ }
+ }
+ }
+ }
+ }
+
+ if (status == F_child) {
+ controller_thread_delete_simple(&thread);
+
+ return F_child;
+ }
+
+ if (F_status_is_error_not(status) && status != F_failure && main->parameters[controller_parameter_validate_e].result == f_console_result_none_e && controller_thread_is_enabled(F_true, &thread)) {
+
+ if (setting->mode == controller_setting_mode_service_e) {
+ controller_thread_join(&thread.id_signal);
+ }
+ else if (setting->mode == controller_setting_mode_program_e) {
+ status = controller_rule_wait_all(global, F_true, F_false, 0);
+ }
+ }
+
+ controller_thread_process_cancel(global, F_true, controller_thread_cancel_call_e, 0);
+
+ controller_thread_process_exit(&global);
+
+ if (thread.id_listen) {
+ f_thread_cancel(thread.id_listen);
+ }
+
+ if (thread.id_signal) f_thread_join(thread.id_signal, 0);
+ if (thread.id_cleanup) f_thread_join(thread.id_cleanup, 0);
+ if (thread.id_control) f_thread_join(thread.id_control, 0);
+ if (thread.id_listen) f_thread_join(thread.id_listen, 0);
+ if (thread.id_entry) f_thread_join(thread.id_entry, 0);
+ if (thread.id_rule) f_thread_join(thread.id_rule, 0);
+
+ thread.id_cleanup = 0;
+ thread.id_control = 0;
+ thread.id_listen = 0;
+ thread.id_entry = 0;
+ thread.id_rule = 0;
+ thread.id_signal = 0;
+
+ controller_thread_delete_simple(&thread);
+
+ if (F_status_is_error(status)) {
+ return F_status_set_error(F_failure);
+ }
+
+ if (F_status_set_fine(status) == F_interrupt) {
+ controller_print_signal_received(main, thread.signal);
+
+ if (main->output.verbosity != f_console_verbosity_quiet_e) {
+ fll_print_terminated(f_string_eol_s, main->output.to.stream);
+ }
+
+ return F_status_set_error(F_interrupt);
+ }
+
+ return F_none;
+ }
+#endif // _di_controller_thread_main_
+
+#ifndef _di_controller_thread_join_
+ f_status_t controller_thread_join(f_thread_id_t * const id) {
+
+ if (!id || !*id) return F_data_not;
+
+ const f_status_t status = f_thread_join(*id, 0);
+
+ if (F_status_is_error_not(status) || F_status_set_fine(status) == F_found_not) {
+ *id = 0;
+ }
+
+ return status;
+ }
+#endif // _di_controller_thread_join_
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
--- /dev/null
+/**
+ * FLL - Level 3
+ *
+ * Project: Controller
+ * API Version: 0.5
+ * Licenses: lgpl-2.1-or-later
+ */
+#ifndef _PRIVATE_thread_h
+#define _PRIVATE_thread_h
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Thread for periodically cleanup data when not busy.
+ *
+ * @param arguments
+ * The thread arguments.
+ * Must be of type controller_global_t.
+ *
+ * @return
+ * 0, always.
+ */
+#ifndef _di_controller_thread_cleanup_
+ extern void * controller_thread_cleanup(void * const arguments) F_attribute_visibility_internal_d;
+#endif // _di_controller_thread_cleanup_
+
+/**
+ * Check to see if thread is enabled for the normal operations like entry and control or for exit operations.
+ *
+ * @param is_normal
+ * If TRUE, then process as if this operates during a normal operation (entry and control).
+ * If FALSE, then process as if this operates during a an exit operation.
+ * @param thread
+ * The thread data.
+ *
+ * @return
+ * TRUE when enabled.
+ * FALSE when disabled.
+ */
+#ifndef _di_controller_thread_is_enabled_
+ extern f_status_t controller_thread_is_enabled(const bool is_normal, controller_thread_t * const thread) F_attribute_visibility_internal_d;
+#endif // _di_controller_thread_is_enabled_
+
+/**
+ * Check to see if thread is enabled for the normal operations like entry and control or for exit operations for some process.
+ *
+ * @param process
+ * The process to use when checking if thread is enabled.
+ * @param thread
+ * The thread data.
+ *
+ * @return
+ *
+ * Success from controller_thread_is_enabled_process_type().
+ *
+ * @see controller_thread_is_enabled_process_type()
+ */
+#ifndef _di_controller_thread_is_enabled_process_
+ extern f_status_t controller_thread_is_enabled_process(controller_process_t * const process, controller_thread_t * const thread) F_attribute_visibility_internal_d;
+#endif // _di_controller_thread_is_enabled_process_
+
+/**
+ * Check to see if thread is enabled for the normal operations like entry and control or for exit operations for some process type.
+ *
+ * @param type
+ * The process type to use when checking if thread is enabled.
+ * @param thread
+ * The thread data.
+ *
+ * @return
+ *
+ * Success from controller_thread_is_enabled().
+ *
+ * @see controller_thread_is_enabled()
+ */
+#ifndef _di_controller_thread_is_enabled_process_type_
+ extern f_status_t controller_thread_is_enabled_process_type(const uint8_t type, controller_thread_t * const thread) F_attribute_visibility_internal_d;
+#endif // _di_controller_thread_is_enabled_process_type_
+
+/**
+ * Start all threads, wait on threads, and handle requests.
+ *
+ * @param main
+ * The main program data.
+ * @param setting
+ * The controller settings data.
+ *
+ * @return
+ * F_none on success.
+ * F_child on child process exiting.
+ *
+ * F_failure (with error bit) on any failure.
+ * F_interrupt (with error bit) on receiving a process signal, such as an interrupt signal.
+ */
+#ifndef _di_controller_thread_main_
+ extern f_status_t controller_thread_main(controller_main_t * const main, controller_setting_t * const setting) F_attribute_visibility_internal_d;
+#endif // _di_controller_thread_main_
+
+/***
+ * Join a thread, assigning id to NULL on success.
+ *
+ * If the ID is not found, then it is also set to NULL.
+ *
+ * @param id
+ * The thread ID.
+ *
+ * @return
+ * F_none on success.
+ *
+ * Success from: f_thread_join().
+ *
+ * Errors (with error bit) from: f_thread_join().
+ *
+ * @see f_thread_join()
+ */
+#ifndef _di_controller_thread_join_
+ extern f_status_t controller_thread_join(f_thread_id_t * const id) F_attribute_visibility_internal_d;
+#endif // _di_controller_thread_join_
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // _PRIVATE_thread_h
--- /dev/null
+#include "../controller/controller.h"
+#include "../common/private-common.h"
+#include "../control/private-control.h"
+#include "../controller/private-controller_print.h"
+#include "private-thread.h"
+#include "private-thread_control.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef _di_controller_thread_control_
+ void * controller_thread_control(void * const arguments) {
+
+ f_thread_cancel_state_set(PTHREAD_CANCEL_DEFERRED, 0);
+
+ const controller_global_t *global = (controller_global_t *) arguments;
+
+ if (global->thread->enabled != controller_thread_enabled_e) return 0;
+
+ f_status_t status = F_none;
+ controller_control_t control = macro_controller_control_t_initialize(&global->setting->control_socket, 0);
+
+ do {
+
+ // Shrink any overly large buffers.
+ if (control.cache_1.size > controller_control_default_socket_cache_d) {
+ status = f_string_dynamic_resize(controller_control_default_socket_cache_d, &control.cache_1);
+ }
+
+ if (F_status_is_error_not(status) && control.cache_2.size > controller_control_default_socket_buffer_d) {
+ status = f_string_dynamic_resize(controller_control_default_socket_buffer_d, &control.cache_2);
+ }
+
+ if (F_status_is_error_not(status) && control.cache_3.size > controller_control_default_socket_buffer_d) {
+ status = f_string_dynamic_resize(controller_control_default_socket_buffer_d, &control.cache_3);
+ }
+
+ if (F_status_is_error_not(status) && control.input.size > controller_control_default_socket_buffer_d) {
+ status = f_string_dynamic_resize(controller_control_default_socket_buffer_d, &control.input);
+ }
+
+ if (F_status_is_error_not(status) && control.output.size > controller_control_default_socket_buffer_d) {
+ status = f_string_dynamic_resize(controller_control_default_socket_buffer_d, &control.output);
+ }
+
+ if (F_status_is_error(status)) {
+ controller_print_error(global->thread, global->main->error, F_status_set_fine(status), "f_string_dynamic_resize", F_true);
+ }
+
+ status = controller_control_accept(global, &control);
+
+ if (F_status_is_error(status)) {
+ controller_print_error(global->thread, global->main->error, F_status_set_fine(status), "controller_control_accept", F_true);
+ }
+
+ } while (F_status_is_fine(status) && status != F_child && global->thread->enabled == controller_thread_enabled_e);
+
+ controller_control_delete_simple(&control);
+
+ return 0;
+ }
+#endif // _di_controller_thread_control_
+
+#ifndef _di_controller_thread_control_listen_
+ void * controller_thread_control_listen(void * const arguments) {
+
+ f_thread_cancel_state_set(PTHREAD_CANCEL_ASYNCHRONOUS, 0);
+
+ const controller_global_t *global = (controller_global_t *) arguments;
+
+ if (global->thread->enabled != controller_thread_enabled_e) return 0;
+
+ if (global->setting->interruptible) {
+ f_signal_mask(SIG_UNBLOCK, &global->main->signal.set, 0);
+ }
+
+ f_socket_t * const server = &global->setting->control_socket;
+
+ const f_status_t status = f_socket_listen(server, controller_control_default_socket_backlog_d);
+
+ if (F_status_is_error(status)) {
+ controller_print_error(global->thread, global->main->error, F_status_set_fine(status), "f_socket_listen", F_true);
+ }
+
+ return 0;
+ }
+#endif // _di_controller_thread_control_listen_
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
--- /dev/null
+/**
+ * FLL - Level 3
+ *
+ * Project: Controller
+ * API Version: 0.5
+ * Licenses: lgpl-2.1-or-later
+ */
+#ifndef _PRIVATE_thread_control_h
+#define _PRIVATE_thread_control_h
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Thread for handling control requests and responses.
+ *
+ * @param arguments
+ * The thread arguments.
+ * Must be of type controller_global_t.
+ *
+ * @return
+ * 0, always.
+ */
+#ifndef _di_controller_thread_control_
+ extern void * controller_thread_control(void * const arguments) F_attribute_visibility_internal_d;
+#endif // _di_controller_thread_control_
+
+/**
+ * Thread for handling the control listener.
+ *
+ * This runs on a separate thread entirely to be interuptable and closable distinctly from the main control thread.
+ * This is simple and has nothing that needs to be cleaned up and so immediately exits on cancel.
+ *
+ * @param arguments
+ * The thread arguments.
+ * Must be of type controller_global_t.
+ *
+ * @return
+ * 0, always.
+ */
+#ifndef _di_controller_thread_control_listen_
+ extern void * controller_thread_control_listen(void * const arguments) F_attribute_visibility_internal_d;
+#endif // _di_controller_thread_control_listen_
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // _PRIVATE_thread_control_h
--- /dev/null
+#include "../controller/controller.h"
+#include "../common/private-common.h"
+#include "../entry/private-entry.h"
+#include "../lock/private-lock_print.h"
+#include "../thread/private-thread.h"
+#include "private-thread_entry.h"
+#include "private-thread_signal.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef _di_controller_thread_entry_
+ void * controller_thread_entry(void * const arguments) {
+
+ f_thread_cancel_state_set(PTHREAD_CANCEL_DEFERRED, 0);
+
+ controller_main_entry_t *entry = (controller_main_entry_t *) arguments;
+
+ if (!controller_thread_is_enabled(F_true, entry->global->thread)) return 0;
+
+ controller_main_t *main = entry->global->main;
+ controller_cache_t *cache = &entry->global->thread->cache;
+ f_status_t *status = &entry->global->thread->status;
+
+ *status = controller_entry_read(*entry->global, F_true, cache);
+
+ if (F_status_set_fine(*status) == F_interrupt) {
+ entry->setting->ready = controller_setting_ready_abort_e;
+ }
+ else if (F_status_is_error(*status)) {
+ entry->setting->ready = controller_setting_ready_fail_e;
+ }
+ else if (*status != F_child) {
+ *status = controller_entry_preprocess(*entry->global, F_true, cache);
+ }
+
+ if (F_status_is_error_not(*status) && *status != F_child) {
+ if (main->parameters[controller_parameter_validate_e].result == f_console_result_none_e || main->parameters[controller_parameter_simulate_e].result == f_console_result_found_e) {
+
+ if (entry->setting->entry.pid == controller_entry_pid_require_e && f_file_exists(entry->setting->path_pid.string) == F_true) {
+ if (main->error.verbosity != f_console_verbosity_quiet_e) {
+ controller_lock_print(main->error.to, entry->global->thread);
+
+ fl_print_format("%c%[%SThe pid file '%]", main->error.to.stream, f_string_eol_s[0], main->error.context, main->error.prefix ? main->error.prefix : f_string_empty_s, main->error.context);
+ fl_print_format("%[%Q%]", main->error.to.stream, main->error.notable, entry->setting->path_pid, main->error.notable);
+ fl_print_format("%[' must not already exist.%]%c", main->error.to.stream, main->error.context, main->error.context, f_string_eol_s[0]);
+
+ controller_unlock_print_flush(main->error.to, entry->global->thread);
+ }
+
+ entry->setting->ready = controller_setting_ready_fail_e;
+ *status = F_status_set_error(F_available_not);
+ }
+ else {
+ *status = controller_entry_process(entry->global, cache, F_false, F_true);
+
+ if (F_status_is_error(*status)) {
+ entry->setting->ready = controller_setting_ready_fail_e;
+
+ if ((F_status_set_fine(*status) == F_execute || F_status_set_fine(*status) == F_require) && entry->global->setting->failsafe_enabled) {
+ const uint8_t original_enabled = entry->global->thread->enabled;
+
+ // Restore operating mode so that the failsafe can execute.
+ *status = f_thread_mutex_lock(&entry->global->thread->lock.alert);
+
+ if (F_status_is_error_not(*status)) {
+ entry->global->thread->enabled = controller_thread_enabled_e;
+
+ f_thread_mutex_unlock(&entry->global->thread->lock.alert);
+ }
+
+ // Restart the signal thread to allow for signals while operating the failsafe Items.
+ if (!entry->global->thread->id_signal) {
+ f_thread_create(0, &entry->global->thread->id_signal, &controller_thread_signal_normal, (void *) entry->global);
+ }
+
+ const f_status_t status_failsafe = controller_entry_process(entry->global, cache, F_true, F_true);
+
+ if (F_status_is_error(status_failsafe)) {
+ if (main->error.verbosity != f_console_verbosity_quiet_e) {
+ controller_lock_print(main->error.to, entry->global->thread);
+
+ fl_print_format("%c%[%SFailed while processing requested failsafe item '%]", main->error.to.stream, f_string_eol_s[0], main->error.context, main->error.prefix ? main->error.prefix : f_string_empty_s, main->error.context);
+ fl_print_format("%[%Q%]", main->error.to.stream, main->error.notable, entry->global->setting->entry.items.array[entry->global->setting->failsafe_enabled].name, main->error.notable);
+ fl_print_format("%['.%]%c", main->error.to.stream, main->error.context, main->error.context, f_string_eol_s[0]);
+
+ controller_unlock_print_flush(main->error.to, entry->global->thread);
+ }
+
+ *status = F_status_set_error(F_failure);
+ }
+ else {
+
+ // Restore operating mode to value prior to failsafe mode.
+ *status = f_thread_mutex_lock(&entry->global->thread->lock.alert);
+
+ if (F_status_is_error_not(*status)) {
+ entry->global->thread->enabled = original_enabled;
+
+ f_thread_mutex_unlock(&entry->global->thread->lock.alert);
+ }
+
+ *status = F_failure;
+ }
+ }
+ }
+ else if (F_status_set_fine(*status) == F_interrupt) {
+ entry->setting->ready = controller_setting_ready_abort_e;
+ }
+ else if (*status != F_child) {
+ entry->setting->ready = controller_setting_ready_done_e;
+ }
+ }
+ }
+ }
+
+ if (*status == F_child) {
+
+ // A forked child process should deallocate memory on exit.
+ // It seems that this function doesn't return to the calling thread for a forked child process, even with the "return 0;" below.
+ // Deallocate as much as possible.
+ controller_thread_delete_simple(entry->global->thread);
+ controller_setting_delete_simple(entry->global->setting);
+ controller_main_delete(entry->global->main);
+
+ // According to the manpages, pthread_exit() calls exit(0), which is not good because a non-zero exit code may be returned.
+ if (main->child) exit(main->child);
+
+ return 0;
+ }
+
+ f_thread_condition_signal_all(&entry->global->thread->lock.alert_condition);
+
+ return 0;
+ }
+#endif // _di_controller_thread_entry_
+
+#ifndef _di_controller_thread_exit_
+ void * controller_thread_exit(void * const arguments) {
+
+ f_thread_cancel_state_set(PTHREAD_CANCEL_DEFERRED, 0);
+
+ controller_main_entry_t *entry = (controller_main_entry_t *) arguments;
+
+ controller_main_t *main = entry->global->main;
+ controller_cache_t *cache = &entry->global->thread->cache;
+ f_status_t *status = &entry->global->thread->status;
+
+ *status = controller_entry_read(*entry->global, F_false, cache);
+
+ if (F_status_set_fine(*status) == F_interrupt) {
+ entry->setting->ready = controller_setting_ready_abort_e;
+ }
+ else if (F_status_is_error(*status)) {
+ entry->setting->ready = controller_setting_ready_fail_e;
+ }
+ else if (*status == F_file_found_not) {
+ entry->setting->ready = controller_setting_ready_done_e;
+ }
+ else if (*status != F_child) {
+ *status = controller_entry_preprocess(*entry->global, F_false, cache);
+ }
+
+ if (F_status_is_error_not(*status) && *status != F_child && *status != F_file_found_not) {
+ if (main->parameters[controller_parameter_validate_e].result == f_console_result_none_e || main->parameters[controller_parameter_simulate_e].result == f_console_result_found_e) {
+
+ *status = controller_entry_process(entry->global, cache, F_false, F_false);
+
+ if (F_status_is_error(*status)) {
+ entry->setting->ready = controller_setting_ready_fail_e;
+
+ if ((F_status_set_fine(*status) == F_execute || F_status_set_fine(*status) == F_require) && entry->global->setting->failsafe_enabled) {
+
+ const uint8_t original_enabled = entry->global->thread->enabled;
+
+ // Restore operating mode so that the failsafe can execute.
+ if (F_status_set_fine(*status) == F_execute) {
+ *status = f_thread_mutex_lock(&entry->global->thread->lock.alert);
+
+ if (F_status_is_error_not(*status)) {
+ entry->global->thread->enabled = controller_thread_enabled_exit_e;
+
+ f_thread_mutex_unlock(&entry->global->thread->lock.alert);
+ }
+
+ // Restart the signal thread to allow for signals while operating the failsafe Items.
+ if (!entry->global->thread->id_signal) {
+ f_thread_create(0, &entry->global->thread->id_signal, &controller_thread_signal_other, (void *) entry->global);
+ }
+ }
+
+ const f_status_t status_failsafe = controller_entry_process(entry->global, cache, F_true, F_false);
+
+ if (F_status_is_error(status_failsafe)) {
+ if (main->error.verbosity != f_console_verbosity_quiet_e) {
+ controller_lock_print(main->error.to, entry->global->thread);
+
+ fl_print_format("%c%[%SFailed while processing requested failsafe item '%]", main->error.to.stream, f_string_eol_s[0], main->error.context, main->error.prefix ? main->error.prefix : f_string_empty_s, main->error.context);
+ fl_print_format("%[%Q%]", main->error.to.stream, main->error.notable, entry->global->setting->entry.items.array[entry->global->setting->failsafe_enabled].name, main->error.notable);
+ fl_print_format("%['.%]%c", main->error.to.stream, main->error.context, main->error.context, f_string_eol_s[0]);
+
+ controller_unlock_print_flush(main->error.to, entry->global->thread);
+ }
+
+ *status = F_status_set_error(F_failure);
+ }
+ else {
+
+ // Restore operating mode to value prior to failsafe mode.
+ *status = f_thread_mutex_lock(&entry->global->thread->lock.alert);
+
+ if (F_status_is_error_not(*status)) {
+ entry->global->thread->enabled = original_enabled;
+
+ f_thread_mutex_unlock(&entry->global->thread->lock.alert);
+ }
+
+ *status = F_failure;
+ }
+ }
+ }
+ else if (F_status_set_fine(*status) == F_interrupt) {
+ entry->setting->ready = controller_setting_ready_abort_e;
+ }
+ else if (*status != F_child) {
+ entry->setting->ready = controller_setting_ready_done_e;
+ }
+ }
+ }
+
+ if (*status == F_child) {
+
+ // A forked child process should deallocate memory on exit.
+ // It seems that this function doesn't return to the calling thread for a forked child process, even with the "return 0;" below.
+ // Deallocate as much as possible.
+
+ controller_thread_delete_simple(entry->global->thread);
+ controller_setting_delete_simple(entry->global->setting);
+ controller_main_delete(entry->global->main);
+
+ return 0;
+ }
+
+ if (F_status_is_error_not(f_thread_mutex_lock(&entry->global->thread->lock.alert))) {
+ entry->global->thread->enabled = controller_thread_enabled_not_e;
+
+ f_thread_mutex_unlock(&entry->global->thread->lock.alert);
+ }
+
+ f_thread_condition_signal_all(&entry->global->thread->lock.alert_condition);
+
+ return 0;
+ }
+#endif // _di_controller_thread_exit_
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
--- /dev/null
+/**
+ * FLL - Level 3
+ *
+ * Project: Controller
+ * API Version: 0.5
+ * Licenses: lgpl-2.1-or-later
+ */
+#ifndef _PRIVATE_thread_entry_h
+#define _PRIVATE_thread_entry_h
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Thread for handling entry processing.
+ *
+ * This acts as the main rule thread during entry processing.
+ * This runs all synchronous rules or spawns asynchronous rules.
+ *
+ * @param arguments
+ * The thread arguments.
+ * Must be of type controller_main_entry_t.
+ *
+ * @return
+ * 0, always.
+ */
+#ifndef _di_controller_thread_entry_
+ extern void * controller_thread_entry(void * const arguments) F_attribute_visibility_internal_d;
+#endif // _di_controller_thread_entry_
+
+/**
+ * Thread for handling exit file processing.
+ *
+ * This acts as the main rule thread during exit processing.
+ * This runs all synchronous rules or spawns asynchronous rules.
+ *
+ * Do not confuse this with exiting a thread, this is the what process the exit files (similar to that of an entry file).
+ * Exit files process the "stop" action, whereas the Entry files process the "start" Action
+ *
+ * @param arguments
+ * The thread arguments.
+ * Must be of type controller_main_entry_t.
+ *
+ * @return
+ * 0, always.
+ */
+#ifndef _di_controller_thread_exit_
+ extern void * controller_thread_exit(void * const arguments) F_attribute_visibility_internal_d;
+#endif // _di_controller_thread_exit_
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // _PRIVATE_thread_entry_h
--- /dev/null
+#include "../controller/controller.h"
+#include "../common/private-common.h"
+#include "../controller/private-controller.h"
+#include "../controller/private-controller_print.h"
+#include "../rule/private-rule.h"
+#include "private-thread.h"
+#include "private-thread_entry.h"
+#include "private-thread_process.h"
+#include "private-thread_signal.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef _di_controller_thread_process_
+ void controller_thread_process(const bool is_normal, controller_process_t * const process) {
+
+ {
+ controller_thread_t *thread = (controller_thread_t *) process->main_thread;
+
+ if (!controller_thread_is_enabled(is_normal, thread)) return;
+ }
+
+ const f_status_t status = controller_rule_process_do(controller_process_option_asynchronous_d, process);
+
+ if (status == F_child) {
+
+ // A forked child process should deallocate memory on exit.
+ // It seems that this function doesn't return to the calling thread for a forked child process, even with the "return 0;" below.
+ // Deallocate as much as possible.
+ controller_main_t *main = (controller_main_t *) process->main_data;
+ controller_setting_t *setting = (controller_setting_t *) process->main_setting;
+ controller_thread_t *thread = (controller_thread_t *) process->main_thread;
+
+ controller_thread_delete_simple(thread);
+ controller_setting_delete_simple(setting);
+ controller_main_delete(main);
+
+ // According to the manpages, pthread_exit() calls exit(0), which is not good because a non-zero exit code may be returned.
+ if (main->child) exit(main->child);
+ }
+ }
+#endif // _di_controller_thread_process_
+
+#ifndef _di_controller_thread_process_cancel_
+ void controller_thread_process_cancel(const controller_global_t global, const bool is_normal, const uint8_t by, controller_process_t * const caller) {
+
+ // Only cancel when enabled.
+ if (!controller_thread_is_enabled(is_normal, global.thread)) {
+ return;
+ }
+
+ // Use the alert lock to toggle enabled (being used as if it were a write like and signal lock).
+ f_status_t status = f_thread_mutex_lock(&global.thread->lock.alert);
+
+ if (F_status_is_error(status)) {
+ global.thread->enabled = controller_thread_enabled_not_e;
+ }
+ else {
+ if (by == controller_thread_cancel_execute_e) {
+ global.thread->enabled = controller_thread_enabled_execute_e;
+ }
+ else if (by == controller_thread_cancel_exit_e) {
+ global.thread->enabled = controller_thread_enabled_not_e;
+ }
+ else if (by == controller_thread_cancel_exit_execute_e) {
+ global.thread->enabled = controller_thread_enabled_exit_execute_e;
+ }
+ else {
+ global.thread->enabled = controller_thread_enabled_exit_e;
+ }
+
+ f_thread_mutex_unlock(&global.thread->lock.alert);
+ }
+
+ f_array_length_t spent = 0;
+
+ struct timespec time;
+
+ controller_process_t *process = 0;
+
+ f_array_length_t i = 0;
+ f_array_length_t j = 0;
+ pid_t pid = 0;
+
+ if (global.thread->id_cleanup) {
+ f_thread_cancel(global.thread->id_cleanup);
+ f_thread_join(global.thread->id_cleanup, 0);
+
+ global.thread->id_cleanup = 0;
+ }
+
+ // The sigtimedwait() function that is run inside of signal must be interrupted via the f_thread_cancel().
+ if (by != controller_thread_cancel_signal_e && global.thread->id_signal) {
+ f_thread_cancel(global.thread->id_signal);
+ f_thread_join(global.thread->id_signal, 0);
+
+ global.thread->id_signal = 0;
+ }
+
+ for (; i < global.thread->processs.used; ++i) {
+
+ if (!global.thread->processs.array[i]) continue;
+ if (caller && i == caller->id) continue;
+
+ process = global.thread->processs.array[i];
+
+ // Do not cancel exit processes, when not performing "execute" during exit.
+ if (process->type == controller_process_type_exit_e && global.thread->enabled != controller_thread_enabled_exit_execute_e) {
+ continue;
+ }
+
+ for (j = 0; j < process->childs.used; ++j) {
+
+ if (process->childs.array[j] > 0) {
+ f_signal_send(global.thread->signal ? global.thread->signal : F_signal_termination, process->childs.array[j]);
+ }
+ } // for
+
+ for (j = 0; j < process->path_pids.used; ++j) {
+
+ if (process->path_pids.array[j].used && f_file_exists(process->path_pids.array[j].string) == F_true) {
+ status = controller_file_pid_read(process->path_pids.array[j], &pid);
+
+ if (pid) {
+ f_signal_send(global.thread->signal ? global.thread->signal : F_signal_termination, pid);
+ }
+ }
+ } // for
+ } // for
+
+ for (i = 0; i < global.thread->processs.size && spent < controller_thread_exit_process_cancel_total_d; ++i) {
+
+ if (!global.thread->processs.array[i]) continue;
+ if (caller && i == caller->id) continue;
+
+ process = global.thread->processs.array[i];
+
+ // Do not cancel exit processes, when not performing "execute" during exit.
+ if (process->type == controller_process_type_exit_e && global.thread->enabled != controller_thread_enabled_exit_execute_e) continue;
+
+ do {
+ if (!process->id_thread) break;
+
+ f_thread_signal(process->id_thread, global.thread->signal ? global.thread->signal : F_signal_termination);
+
+ controller_time(0, controller_thread_exit_process_cancel_wait_d, &time);
+
+ status = f_thread_join_timed(process->id_thread, time, 0);
+
+ if (status == F_none) {
+ for (j = 0; j < process->childs.size; ++j) {
+ process->childs.array[j] = 0;
+ } // for
+
+ process->childs.used = 0;
+ process->id_thread = 0;
+ }
+
+ ++spent;
+
+ } while (status == F_time && spent < controller_thread_exit_process_cancel_total_d);
+
+ if (process->path_pids.used) {
+ for (j = 0; j < process->path_pids.used; ++j) {
+
+ for (; spent < controller_thread_exit_process_cancel_total_d; ++spent) {
+
+ if (process->path_pids.array[j].used && f_file_exists(process->path_pids.array[j].string) == F_true) {
+ status = controller_file_pid_read(process->path_pids.array[j], &pid);
+
+ if (pid) {
+
+ // A hackish way to determine if the pid exists while waiting.
+ if (getpgid(pid) >= 0) {
+ time.tv_sec = 0;
+ time.tv_nsec = controller_thread_exit_process_cancel_wait_d;
+
+ nanosleep(&time, 0);
+
+ continue;
+ }
+ else {
+ f_file_remove(process->path_pids.array[j].string);
+ process->path_pids.array[j].used = 0;
+ }
+ }
+ }
+
+ break;
+ } // for
+ } // for
+ }
+ } // for
+
+ for (i = 0; i < global.thread->processs.size; ++i) {
+
+ if (!global.thread->processs.array[i]) continue;
+ if (caller && i == caller->id) continue;
+
+ process = global.thread->processs.array[i];
+
+ // Do not kill exit processes, when not performing "execute" during exit.
+ if (process->type == controller_process_type_exit_e && global.thread->enabled != controller_thread_enabled_exit_execute_e) continue;
+
+ if (process->id_thread) {
+ if (process->childs.used) {
+ for (j = 0; j < process->childs.used; ++j) {
+
+ if (process->childs.array[j] > 0) {
+ f_signal_send(F_signal_kill, process->childs.array[j]);
+
+ time.tv_sec = 0;
+ time.tv_nsec = controller_thread_exit_process_cancel_wait_d;
+
+ process->childs.array[j] = 0;
+ }
+ } // for
+
+ nanosleep(&time, 0);
+ }
+
+ f_thread_join(process->id_thread, 0);
+
+ process->id_thread = 0;
+ }
+
+ for (j = 0; j < process->childs.size; ++j) {
+ process->childs.array[j] = 0;
+ } // for
+
+ process->childs.used = 0;
+
+ for (j = 0; j < process->path_pids.used; ++j) {
+
+ if (f_file_exists(process->path_pids.array[j].string) == F_true) {
+ status = controller_file_pid_read(process->path_pids.array[j], &pid);
+
+ if (pid) {
+ f_signal_send(F_signal_kill, pid);
+ }
+
+ f_file_remove(process->path_pids.array[j].string);
+ process->path_pids.array[j].used = 0;
+ }
+ } // for
+
+ process->path_pids.used = 0;
+ } // for
+ }
+#endif // _di_controller_thread_process_cancel_
+
+#ifndef _di_controller_thread_process_exit_
+ void controller_thread_process_exit(controller_global_t * const global) {
+
+ if (global->thread->enabled != controller_thread_enabled_exit_e) {
+ return;
+ }
+
+ if (global->setting->ready == controller_setting_ready_done_e) {
+
+ // The exit processing runs using the entry thread.
+ if (global->thread->id_entry) {
+ f_thread_cancel(global->thread->id_entry);
+ f_thread_join(global->thread->id_entry, 0);
+
+ global->thread->id_entry = 0;
+ }
+
+ // Restart the signal thread to allow for signals while operating the Exit.
+ if (!global->thread->id_signal) {
+ f_thread_create(0, &global->thread->id_signal, &controller_thread_signal_other, (void *) global);
+ }
+
+ const controller_main_entry_t entry = macro_controller_main_entry_t_initialize(global, global->setting);
+
+ f_status_t status = f_thread_create(0, &global->thread->id_entry, &controller_thread_exit, (void *) &entry);
+
+ if (F_status_is_error(status)) {
+ if (global->main->error.verbosity != f_console_verbosity_quiet_e) {
+ controller_print_error(global->thread, global->main->error, F_status_set_fine(status), "f_thread_create", F_true);
+ }
+
+ if (F_status_is_error_not(f_thread_mutex_lock(&global->thread->lock.alert))) {
+ global->thread->enabled = controller_thread_enabled_not_e;
+
+ f_thread_mutex_unlock(&global->thread->lock.alert);
+ }
+ else {
+ global->thread->enabled = controller_thread_enabled_not_e;
+ }
+ }
+ else {
+ struct timespec time;
+
+ do {
+ status = f_thread_mutex_lock(&global->thread->lock.alert);
+
+ if (F_status_is_error(status)) {
+ global->thread->enabled = controller_thread_enabled_not_e;
+
+ break;
+ }
+
+ controller_time(controller_thread_exit_ready_timeout_seconds_d, controller_thread_exit_ready_timeout_nanoseconds_d, &time);
+
+ status = f_thread_condition_wait_timed(&time, &global->thread->lock.alert_condition, &global->thread->lock.alert);
+
+ f_thread_mutex_unlock(&global->thread->lock.alert);
+
+ } while (F_status_is_error_not(status) && global->thread->enabled == controller_thread_enabled_exit_e);
+
+ if (F_status_is_error(status)) {
+ if (F_status_is_error_not(f_thread_mutex_lock(&global->thread->lock.alert))) {
+ global->thread->enabled = controller_thread_enabled_not_e;
+
+ f_thread_mutex_unlock(&global->thread->lock.alert);
+ }
+ else {
+ global->thread->enabled = controller_thread_enabled_not_e;
+ }
+ }
+ }
+
+ // The sigtimedwait() function that is run inside of signal must be interrupted via the f_thread_cancel().
+ if (global->thread->id_signal) {
+ f_thread_cancel(global->thread->id_signal);
+ f_thread_join(global->thread->id_signal, 0);
+
+ global->thread->id_signal = 0;
+ }
+
+ controller_thread_process_cancel(*global, F_false, controller_thread_cancel_exit_e, 0);
+ }
+ else {
+ if (F_status_is_error_not(f_thread_mutex_lock(&global->thread->lock.alert))) {
+ global->thread->enabled = controller_thread_enabled_not_e;
+
+ f_thread_mutex_unlock(&global->thread->lock.alert);
+ }
+ else {
+ global->thread->enabled = controller_thread_enabled_not_e;
+ }
+ }
+ }
+#endif // _di_controller_thread_process_exit_
+
+#ifndef _di_controller_thread_process_normal_
+ void * controller_thread_process_normal(void * const arguments) {
+
+ f_thread_cancel_state_set(PTHREAD_CANCEL_DEFERRED, 0);
+
+ controller_thread_process(F_true, (controller_process_t *) arguments);
+
+ return 0;
+ }
+#endif // _di_controller_thread_process_normal_
+
+#ifndef _di_controller_thread_process_other_
+ void * controller_thread_process_other(void * const arguments) {
+
+ f_thread_cancel_state_set(PTHREAD_CANCEL_DEFERRED, 0);
+
+ controller_thread_process(F_false, (controller_process_t *) arguments);
+
+ return 0;
+ }
+#endif // _di_controller_thread_process_other_
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
--- /dev/null
+/**
+ * FLL - Level 3
+ *
+ * Project: Controller
+ * API Version: 0.5
+ * Licenses: lgpl-2.1-or-later
+ */
+#ifndef _PRIVATE_thread_process_h
+#define _PRIVATE_thread_process_h
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Asynchronously execute a Rule process.
+ *
+ * @param is_normal
+ * If TRUE, then process as if this operates during a normal operation (entry and control).
+ * If FALSE, then process as if this operates during a an exit operation.
+ * @param process
+ * The process data.
+ *
+ * @see controller_rule_process_do()
+ */
+#ifndef _di_controller_thread_process_
+ extern void controller_thread_process(const bool is_normal, controller_process_t * const process) F_attribute_visibility_internal_d;
+#endif // _di_controller_thread_process_
+
+/**
+ * Cancel all process threads.
+ *
+ * @param global
+ * The global thread data.
+ * @param is_normal
+ * If TRUE, then process as if this operates during a normal operation (entry and control).
+ * If FALSE, then process as if this operates during a an exit operation.
+ * @param by
+ * Designate the way in which the cancellation should operate.
+ *
+ * If controller_thread_cancel_signal_e, then this was called from within the signal handling thread, so do not cancel the signal thread.
+ * If controller_thread_cancel_call_e, then this was not called from within the signal handling thread, so cancel the signal thread.
+ * If controller_thread_cancel_execute_e, then this was called from within the Entry/Exit for executing a process, so cancel the signal thread but not the Entry thread.
+ * @param caller
+ * (optional) The process that is calling the cancel so that this process itself does not get cancelled.
+ * Set to NULL to not use.
+ */
+#ifndef _di_controller_thread_process_cancel_
+ extern void controller_thread_process_cancel(const controller_global_t global, const bool is_normal, const uint8_t by, controller_process_t * const caller) F_attribute_visibility_internal_d;
+#endif // _di_controller_thread_process_cancel_
+
+/**
+ * Process the Exit file, if applicable.
+ *
+ * @param global
+ * The global thread data.
+ */
+#ifndef _di_controller_thread_process_exit_
+ extern void controller_thread_process_exit(controller_global_t * const global) F_attribute_visibility_internal_d;
+#endif // _di_controller_thread_process_exit_
+
+/**
+ * Asynchronously execute a Rule process during normal operations.
+ *
+ * @param arguments
+ * The thread arguments.
+ * Must be of type controller_process_t.
+ *
+ * @return
+ * 0, always.
+ *
+ * @see controller_thread_process()
+ */
+#ifndef _di_controller_thread_process_normal_
+ extern void * controller_thread_process_normal(void * const arguments) F_attribute_visibility_internal_d;
+#endif // _di_controller_thread_process_normal_
+
+/**
+ * Asynchronously execute a Rule process during other operations.
+ *
+ * @param arguments
+ * The thread arguments.
+ * Must be of type controller_process_t.
+ *
+ * @return
+ * 0, always.
+ *
+ * @see controller_thread_process()
+ */
+#ifndef _di_controller_thread_process_other_
+ extern void * controller_thread_process_other(void * const arguments) F_attribute_visibility_internal_d;
+#endif // _di_controller_thread_process_other_
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // _PRIVATE_thread_process_h
--- /dev/null
+#include "../controller/controller.h"
+#include "../common/private-common.h"
+#include "private-thread.h"
+#include "private-thread_rule.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef _di_controller_thread_rule_
+ void * controller_thread_rule(void * const arguments) {
+
+ f_thread_cancel_state_set(PTHREAD_CANCEL_DEFERRED, 0);
+
+ const controller_global_t *global = (controller_global_t *) arguments;
+
+ if (!controller_thread_is_enabled(F_true, global->thread)) return 0;
+
+ return 0;
+ }
+#endif // _di_controller_thread_rule_
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
--- /dev/null
+/**
+ * FLL - Level 3
+ *
+ * Project: Controller
+ * API Version: 0.5
+ * Licenses: lgpl-2.1-or-later
+ */
+#ifndef _PRIVATE_thread_rule_h
+#define _PRIVATE_thread_rule_h
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Thread for handling rule processing.
+ *
+ * This acts as the main rule thread after entry processing.
+ * This runs all synchronous rules or spawns asynchronous rules.
+ *
+ * @todo the control thread should send commands to this thread, somehow.
+ *
+ * @param arguments
+ * The thread arguments.
+ * Must be of type controller_global_t.
+ *
+ * @return
+ * 0, always.
+ */
+#ifndef _di_controller_thread_rule_
+ extern void * controller_thread_rule(void * const arguments) F_attribute_visibility_internal_d;
+#endif // _di_controller_thread_rule_
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // _PRIVATE_thread_rule_h
--- /dev/null
+#include "../controller/controller.h"
+#include "../common/private-common.h"
+#include "../controller/private-controller.h"
+#include "private-thread.h"
+#include "private-thread_entry.h"
+#include "private-thread_process.h"
+#include "private-thread_signal.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef _di_controller_thread_signal_
+ void controller_thread_signal(controller_global_t * const global, const bool is_normal) {
+
+ if (!controller_thread_is_enabled(is_normal, global->thread)) return;
+
+ siginfo_t information;
+ struct timespec time;
+ int error = 0;
+
+ while (controller_thread_is_enabled(is_normal, global->thread)) {
+
+ controller_time(controller_thread_exit_ready_timeout_seconds_d, controller_thread_exit_ready_timeout_nanoseconds_d, &time);
+
+ error = sigtimedwait(&global->main->signal.set, &information, &time);
+
+ if (error == -1) {
+ if (errno == EAGAIN) continue;
+ }
+
+ if (global->setting->interruptible) {
+ if (information.si_signo == F_signal_interrupt || information.si_signo == F_signal_abort || information.si_signo == F_signal_quit || information.si_signo == F_signal_termination) {
+ global->thread->signal = information.si_signo;
+
+ controller_thread_process_cancel(*global, is_normal, controller_thread_cancel_signal_e, 0);
+
+ break;
+ }
+ }
+ } // while
+ }
+#endif // _di_controller_thread_signal_
+
+#ifndef _di_controller_thread_signal_state_fss_
+ f_status_t controller_thread_signal_state_fss(void * const state, void * const internal) {
+
+ if (!state) {
+ return F_interrupt_not;
+ }
+
+ f_state_t *state_ptr = (f_state_t *) state;
+
+ if (!state_ptr->custom) {
+ return F_interrupt_not;
+ }
+
+ controller_state_interrupt_t *custom = (controller_state_interrupt_t *) state_ptr->custom;
+ controller_thread_t *thread = custom->thread;
+
+ if (!controller_thread_is_enabled(custom->is_normal, thread)) {
+ return F_status_set_error(F_interrupt);
+ }
+
+ if (thread->signal == F_signal_interrupt || thread->signal == F_signal_abort || thread->signal == F_signal_quit || thread->signal == F_signal_termination) {
+ return F_status_set_error(F_interrupt);
+ }
+
+ return F_interrupt_not;
+ }
+#endif // _di_controller_thread_signal_state_fss_
+
+#ifndef _di_controller_thread_signal_state_iki_
+ f_status_t controller_thread_signal_state_iki(void * const state, void * const internal) {
+
+ if (!state) {
+ return F_interrupt_not;
+ }
+
+ f_state_t *state_ptr = (f_state_t *) state;
+
+ if (!state_ptr->custom) {
+ return F_interrupt_not;
+ }
+
+ controller_state_interrupt_t *custom = (controller_state_interrupt_t *) state_ptr->custom;
+ controller_thread_t *thread = custom->thread;
+
+ if (!controller_thread_is_enabled(custom->is_normal, thread)) {
+ return F_status_set_error(F_interrupt);
+ }
+
+ if (thread->signal == F_signal_interrupt || thread->signal == F_signal_abort || thread->signal == F_signal_quit || thread->signal == F_signal_termination) {
+ return F_status_set_error(F_interrupt);
+ }
+
+ return F_interrupt_not;
+ }
+#endif // _di_controller_thread_signal_state_iki_
+
+#ifndef _di_controller_thread_signal_normal_
+ void * controller_thread_signal_normal(void * const arguments) {
+
+ f_thread_cancel_state_set(PTHREAD_CANCEL_DEFERRED, 0);
+
+ controller_thread_signal((controller_global_t *) arguments, F_true);
+
+ return 0;
+ }
+#endif // _di_controller_thread_signal_normal_
+
+#ifndef _di_controller_thread_signal_other_
+ void * controller_thread_signal_other(void * const arguments) {
+
+ f_thread_cancel_state_set(PTHREAD_CANCEL_DEFERRED, 0);
+
+ controller_thread_signal((controller_global_t *) arguments, F_false);
+
+ return 0;
+ }
+#endif // _di_controller_thread_signal_other_
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
--- /dev/null
+/**
+ * FLL - Level 3
+ *
+ * Project: Controller
+ * API Version: 0.5
+ * Licenses: lgpl-2.1-or-later
+ */
+#ifndef _PRIVATE_thread_signal_h
+#define _PRIVATE_thread_signal_h
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Thread for handling signals/interrupts.
+ *
+ * @param global
+ * The global data.
+ * @param is_normal
+ * If TRUE, then process as if this operates during a normal operation (entry and control).
+ * If FALSE, then process as if this operates during a an exit operation.
+ */
+#ifndef _di_controller_thread_signal_
+ extern void controller_thread_signal(controller_global_t * const global, const bool is_normal) F_attribute_visibility_internal_d;
+#endif // _di_controller_thread_signal_
+
+/**
+ * Callback passed to FSS functions for checking for interrupts.
+ *
+ * @param state
+ * The f_state_t data.
+ * @param internal
+ * Not used.
+ *
+ * @return
+ * F_interrupt_not if not interrupted.
+ *
+ * F_interrupt (with error bit) if interrupted.
+ */
+#ifndef _di_controller_thread_signal_state_fss_
+ extern f_status_t controller_thread_signal_state_fss(void * const state, void * const internal) F_attribute_visibility_internal_d;
+#endif // _di_controller_thread_signal_state_fss_
+
+/**
+ * Callback passed to IKI functions for checking for interrupts.
+ *
+ * @param state
+ * The f_state_t data.
+ * @param internal
+ * Not used.
+ *
+ * @return
+ * F_interrupt_not if not interrupted.
+ *
+ * F_interrupt (with error bit) if interrupted.
+ */
+#ifndef _di_controller_thread_signal_state_iki_
+ extern f_status_t controller_thread_signal_state_iki(void * const state, void * const internal) F_attribute_visibility_internal_d;
+#endif // _di_controller_thread_signal_state_iki_
+
+/**
+ * Thread for handling signals/interrupts during normal operations.
+ *
+ * Currently this only handles signals to exist, but may be updated to handle interrupt and hangup signals.
+ *
+ * @param arguments
+ * The thread arguments.
+ * Must be of type controller_global_t.
+ *
+ * @return
+ * 0, always.
+ *
+ * @see controller_thread_signal()
+ */
+#ifndef _di_controller_thread_signal_normal_
+ extern void * controller_thread_signal_normal(void * const arguments) F_attribute_visibility_internal_d;
+#endif // _di_controller_thread_signal_normal_
+
+/**
+ * Thread for handling signals/interrupts during other operations.
+ *
+ * Currently this only handles signals to exist, but may be updated to handle interrupt and hangup signals.
+ *
+ * @param arguments
+ * The thread arguments.
+ * Must be of type controller_global_t.
+ *
+ * @return
+ * 0, always.
+ *
+ * @see controller_thread_signal()
+ */
+#ifndef _di_controller_thread_signal_other_
+ extern void * controller_thread_signal_other(void * const arguments) F_attribute_visibility_internal_d;
+#endif // _di_controller_thread_signal_other_
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // _PRIVATE_thread_signal_h
build_libraries-monolithic -lfll
build_libraries_shared
build_libraries_static
-build_sources_library controller.c private-common.c private-process.c
-build_sources_library common/private-cache.c common/private-control.c common/private-entry.c common/private-lock.c common/private-process.c common/private-rule.c common/private-setting.c common/private-task.c common/private-thread.c
-build_sources_library private-control.c private-controller.c private-entry.c private-rule.c private-task.c
-build_sources_library private-control_print.c private-controller_print.c private-entry_print.c private-lock.c private-lock_print.c private-rule_print.c private-task_print.c
-build_sources_library private-thread.c private-thread_control.c private-thread_entry.c private-thread_process.c private-thread_rule.c private-thread_signal.c
+build_sources_library common/private-common.c common/private-cache.c common/private-control.c common/private-entry.c common/private-lock.c common/private-process.c common/private-rule.c common/private-setting.c common/private-task.c common/private-thread.c
+build_sources_library control/private-control.c control/private-control_print.c
+build_sources_library controller/controller.c controller/private-controller.c controller/private-controller_print.c
+build_sources_library entry/private-entry.c entry/private-entry_print.c
+build_sources_library rule/private-rule.c rule/private-rule_print.c
+build_sources_library process/private-process.c
+build_sources_library task/private-task.c task/private-task_print.c
+build_sources_library lock/private-lock.c lock/private-lock_print.c
+build_sources_library thread/private-thread.c thread/private-thread_control.c thread/private-thread_entry.c thread/private-thread_process.c thread/private-thread_rule.c thread/private-thread_signal.c
+build_sources_library
+build_sources_library
+build_sources_library
build_sources_library_shared
build_sources_library_static
build_sources_program main.c
--- /dev/null
+# fss-0002
+
+Packet Documentation:
+ Todo write this.
--- /dev/null
+# fss-0002
+
+Entry Specification:
+ todo write this.