]> Kevux Git Server - controller/commitdiff
Progress: Continue migrating the project.
authorKevin Day <Kevin@kevux.org>
Sat, 1 Jun 2024 05:50:46 +0000 (00:50 -0500)
committerKevin Day <Kevin@kevux.org>
Sat, 1 Jun 2024 05:50:46 +0000 (00:50 -0500)
20 files changed:
data/build/settings
sources/c/controller/controller.c
sources/c/controller/controller.h
sources/c/controller/main.c
sources/c/init/init.c
sources/c/init/init.h
sources/c/init/main.c
sources/c/main/common.c
sources/c/main/common/enumeration.h
sources/c/main/common/print.c
sources/c/main/common/print.h
sources/c/main/common/type/global.h
sources/c/main/common/type/instance.h
sources/c/main/common/type/thread.h
sources/c/main/controller.h
sources/c/main/print/error.c
sources/c/main/print/error.h
sources/c/main/process.c [new file with mode: 0644]
sources/c/main/process.h [new file with mode: 0644]
sources/c/main/thread/entry.c

index 30b25a6030302daea7e87a112b5775eae43d9bd0..64cb84791a96d60151c727b9e1e319aa5be5f34b 100644 (file)
@@ -43,7 +43,7 @@ build_libraries-monolithic -lfll
 build_sources_library main/common.c main/common/define.c main/common/enumeration.c main/common/print.c main/common/string.c main/common/type.c
 build_sources_library main/common/type/cache.c main/common/type/control.c main/common/type/entry.c main/common/type/execute.c main/common/type/global.c main/common/type/lock.c main/common/type/instance.c main/common/type/program.c main/common/type/rule.c main/common/type/thread.c
 build_sources_library main/common/string/general.c main/common/string/rule.c
-build_sources_library main/instance.c main/path.c
+build_sources_library main/instance.c main/path.c main/process.c
 build_sources_library main/rule.c main/rule/action.c main/rule/execute.c main/rule/expand.c main/rule/instance.c main/rule/is.c main/rule/item.c main/rule/parameter.c main/rule/read.c main/rule/setting.c main/rule/validate.c main/rule/wait.c
 build_sources_library main/print/action.c main/print/data.c main/print/debug.c main/print/error.c main/print/lock.c main/print/message.c main/print/rule.c main/print/verbose.c main/print/warning.c
 build_sources_library main/print/rule/action.c main/print/rule/item.c main/print/rule/setting.c
@@ -55,7 +55,7 @@ build_sources_headers main/common/define/control.h main/common/define/entry.h ma
 build_sources_headers main/common/enumeration/control.h main/common/enumeration/entry.h main/common/enumeration/instance.h main/common/enumeration/program.h main/common/enumeration/rule.h main/common/enumeration/thread.h
 build_sources_headers main/common/string/general.h main/common/string/rule.h
 build_sources_headers main/common/type/cache.h main/common/type/control.h main/common/type/entry.h main/common/type/execute.h main/common/type/global.h main/common/type/lock.h main/common/type/instance.h main/common/type/program.h main/common/type/rule.h main/common/type/thread.h
-build_sources_headers main/instance.h main/path.h
+build_sources_headers main/instance.h main/path.h main/process.h
 build_sources_headers main/rule.h main/rule/action.h main/rule/execute.h main/rule/expand.h main/rule/instance.h main/rule/is.h main/rule/item.h main/rule/parameter.h main/rule/read.h main/rule/setting.h main/rule/validate.h main/rule/wait.h
 build_sources_headers main/print/action.h main/print/data.h main/print/debug.h main/print/error.h main/print/lock.h main/print/message.h main/print/rule.h main/print/verbose.h main/print/warning.h
 build_sources_headers main/print/rule/action.h main/print/rule/item.h main/print/rule/setting.h
index f81b2bd5a661b097c8cf42fdf044fb37788792b5..d6b466cc16438efbddcfb8f7b5e14184e2413ab0 100644 (file)
@@ -25,7 +25,7 @@ extern "C" {
       return;
     }
 
-    // @todo controller_main(&data, &program); (also needs to include things like controller_main_thread())
+    controller_main_process(data, program);
     if (main->setting.state.status == F_status_set_error(F_child)) return;
 
     if (main->setting.state.status == F_status_set_error(F_interrupt)) {
index e7184c89191693ad3c33582342bb37aea37d6bb5..1d3fb9a009cfeed845fe2fda1bc0494375ae6b6e 100644 (file)
@@ -47,6 +47,8 @@ extern "C" {
  *   A pointer to the current program settings.
  *
  *   Must not be NULL.
+ *
+ * @see controller_main_process()
  */
 #ifndef _di_controller_controller_main_
   extern void controller_controller_main(controller_main_t * const main, controller_program_t * const program);
index 1e4ae6b59c50faaab694f4c575c195e77f9b7ff4..6dd82da81520b68806ba4ad62406ad8a71ccb9c6 100644 (file)
@@ -38,7 +38,7 @@ int main(const int argc, const f_string_t *argv, const f_string_t *envp) {
     controller_main_setting_load(arguments, &data, &program);
   }
 
-  controller_controller_main(&data, &program);
+  controller_main_process(&data, &program);
 
   controller_main_delete(&data);
 
index 8c594c33bc8f84b848471f46c54bfa41163fd5fe..14255cd2f354854c7d7a8dd0fe2cf83a6250383a 100644 (file)
@@ -25,7 +25,7 @@ extern "C" {
       return;
     }
 
-    // @todo controller_main(&data, &program);
+    controller_main_process(data, program);
     if (main->setting.state.status == F_status_set_error(F_child)) return;
 
     if (main->setting.state.status == F_status_set_error(F_interrupt)) {
index 5281b47366cbe8a77fa1a09c685c8dfe12e586a7..244a568511c468ab0edc6869ac81fb6150a6dfa3 100644 (file)
@@ -23,6 +23,8 @@ extern "C" {
 /**
  * Execute main program.
  *
+ * @todo this may or may not be needed. For now controller_main_process() is being written as a complete replacement to this.
+ *
  * If main.signal is non-zero, then this blocks and handles the following signals:
  *   - F_signal_abort
  *   - F_signal_broken_pipe
@@ -45,6 +47,8 @@ extern "C" {
  *   A pointer to the current program settings.
  *
  *   Must not be NULL.
+ *
+ * @see controller_main_process()
  */
 #ifndef _di_controller_init_main_
   extern void controller_init_main(controller_main_t * const main, controller_program_t * const program);
index 64c3bf521c261d95196a000555fddb6ea3142014..fde1ef2d23ad27268bd34e9800c03f6f8927a200 100644 (file)
@@ -41,7 +41,7 @@ int main(const int argc, const f_string_t *argv, const f_string_t *envp) {
     controller_main_setting_load(arguments, &data, &program);
   }
 
-  controller_init_main(&data, &program);
+  controller_main_process(&data, &program);
 
   fll_program_standard_set_down(&data.program);
 
index c621ae648d2213b5859f05bfc6fb9909f5705a9c..7a114ac36c7c87f9c83ef7c9554c3ff5bca2dac6 100644 (file)
@@ -176,20 +176,21 @@ extern "C" {
       index = main->program.parameters.array[controller_parameter_settings_e].values.array[main->program.parameters.array[controller_parameter_settings_e].values.used - 1];
 
       controller_path_canonical_relative(main, program->path_current, args[index], &program->path_setting);
+
+      if (F_status_is_error(main->setting.state.status)) {
+        controller_main_print_error_file(&main->program.error, macro_controller_f(controller_path_canonical_relative), args[index], f_file_operation_verify_s, fll_error_file_type_path_e);
+
+        return;
+      }
     }
     else {
       main->setting.state.status = f_string_dynamic_append(controller_default_path_settings_s, &program->path_setting);
-    }
 
-    if (F_status_is_error(main->setting.state.status)) {
-      if (main->program.parameters.array[controller_parameter_settings_e].locations.used) {
-        controller_main_print_error_file(&main->program.error, macro_controller_f(controller_path_canonical_relative), args[index], f_file_operation_verify_s, fll_error_file_type_path_e);
-      }
-      else {
+      if (F_status_is_error(main->setting.state.status)) {
         controller_main_print_error(&main->program.error, macro_controller_f(f_string_dynamic_append));
-      }
 
-      return;
+        return;
+      }
     }
 
     if (!program->path_pid.used && !main->program.parameters.array[controller_parameter_pid_e].locations.used) {
@@ -242,6 +243,27 @@ extern "C" {
         controller_main_print_debug_directory_path_empty(&main->program.warning, f_console_symbol_long_normal_s, controller_long_cgroup_s);
       }
     }
+    else {
+      main->setting.state.status = f_string_dynamic_append_nulless(f_control_group_path_system_prefix_s, &program->path_cgroup);
+
+      if (F_status_is_error_not(main->setting.state.status)) {
+        main->setting.state.status = f_string_dynamic_append_nulless(f_control_group_path_system_default_s, &program->path_cgroup);
+      }
+
+      if (F_status_is_error(main->setting.state.status)) {
+        controller_main_print_error(&main->program.error, macro_controller_f(f_string_dynamic_append_nulless));
+
+        return;
+      }
+
+      main->setting.state.status = f_string_dynamic_append_assure(f_path_separator_s, &program->path_cgroup);
+
+      if (F_status_is_error(main->setting.state.status)) {
+        controller_main_print_error(&main->program.error, macro_controller_f(f_string_dynamic_append_assure));
+
+        return;
+      }
+    }
 
     if (main->program.parameters.array[controller_parameter_interruptible_e].result & f_console_result_found_e) {
       if (main->program.parameters.array[controller_parameter_uninterruptible_e].result & f_console_result_found_e) {
@@ -259,6 +281,14 @@ extern "C" {
     else if (main->program.parameters.array[controller_parameter_uninterruptible_e].result & f_console_result_found_e) {
       main->setting.flag &= ~controller_main_flag_interruptible_e;
     }
+
+    if (main->program.parameters.array[f_console_standard_parameter_daemon_e].result & f_console_result_found_e) {
+      main->setting.flag |= controller_main_flag_daemon_e;
+    }
+
+    if (main->program.parameters.array[controller_parameter_validate_e].result & f_console_result_found_e) {
+      main->setting.flag |= controller_main_flag_validate_e;
+    }
   }
 #endif // _di_controller_main_setting_load_
 
index 345558f8a35749352153ef45266d630b0629e474..283b56521cbf0d70bfcf2afcf38345c74ec5cbd1 100644 (file)
@@ -24,11 +24,13 @@ extern "C" {
  * controller_main_flag_*_e:
  *   - none:                   No flags set.
  *   - copyright:              Print copyright.
+ *   - daemon:                 Run the process in the background and create a PID file.
  *   - error:                  Check if status is "error".
  *   - fine:                   Check if status is "fine".
  *   - help:                   Print help.
  *   - interruptible:          The process is interruptible.
  *   - pipe:                   Use the input pipe.
+ *   - validate:               Perform validation of rules rather than execution.
  *   - version:                Print version.
  *   - version_copyright_help: A helper flag representing version, copyright, and help flag bits being set.
  *   - warning:                Check if status is "warning".
@@ -37,14 +39,16 @@ extern "C" {
   enum {
     controller_main_flag_none_e                   = 0x0,
     controller_main_flag_copyright_e              = 0x1,
-    controller_main_flag_error_e                  = 0x2,
-    controller_main_flag_fine_e                   = 0x4,
-    controller_main_flag_help_e                   = 0x8,
-    controller_main_flag_interruptible_e          = 0x10,
-    controller_main_flag_pipe_e                   = 0x20,
-    controller_main_flag_version_e                = 0x40,
-    controller_main_flag_version_copyright_help_e = 0x49,
-    controller_main_flag_warning_e                = 0x80,
+    controller_main_flag_daemon_e                 = 0x2,
+    controller_main_flag_error_e                  = 0x4,
+    controller_main_flag_fine_e                   = 0x8,
+    controller_main_flag_help_e                   = 0x10,
+    controller_main_flag_interruptible_e          = 0x20,
+    controller_main_flag_pipe_e                   = 0x40,
+    controller_main_flag_validate_e               = 0x80,
+    controller_main_flag_version_e                = 0x100,
+    controller_main_flag_version_copyright_help_e = 0x111,
+    controller_main_flag_warning_e                = 0x200,
   }; // enum
 #endif // _di_controller_main_flag_e_
 
index 4dd7aba5a09784ff419d03a3ba052d3d6cb6ee84..6d597f48cab2122838d8e9aee2bc3eed8c857d6a 100644 (file)
@@ -21,6 +21,7 @@ extern "C" {
     "f_rip_dynamic_partial_nulless",
     "f_string_append_assure",
     "f_string_dynamic_append",
+    "f_string_dynamic_append_assure",
     "f_string_dynamic_append_nulless",
     "f_string_dynamic_partial_append",
     "f_string_dynamic_partial_append_nulless",
index 7517992aef44a202879926c7404b5123b415a9d4..dc436ad91ace56fec8f64f433673bcc8038de6b9 100644 (file)
@@ -54,6 +54,7 @@ extern "C" {
     controller_f_f_rip_dynamic_partial_nulless_e,
     controller_f_f_string_append_assure_e,
     controller_f_f_string_dynamic_append_e,
+    controller_f_f_string_dynamic_append_assure_e,
     controller_f_f_string_dynamic_append_nulless_e,
     controller_f_f_string_dynamic_partial_append_e,
     controller_f_f_string_dynamic_partial_append_nulless_e,
index 77c8614105231e5a9a6db78a52012192e074ed90..4e3a7aaf917529df87db611bd2356085b13b0749 100644 (file)
@@ -19,15 +19,16 @@ extern "C" {
 /**
  * A wrapper used for passing a common set of all data, particularly for sharing between threads.
  *
- * main:    The main program data.
- * program: The program data.
- * thread:  The thread data for a specific thread.
+ * Properties:
+ *   - main:    The main program data.
+ *   - program: The program data.
+ *   - thread:  The thread data for a specific thread.
  *
- * message: A message printer, with custom set to this structure.
- * output:  An output printer, with custom set to this structure.
- * error:   An error printer, with custom set to this structure.
- * warning: A warning printer, with custom set to this structure.
- * debug:   A debug printer, with custom set to this structure.
+ *   - message: The output file for normal output messages (often stdout), but with custom set to (controller_global_t *).
+ *   - output:  The output file for normal/non-message output, aka data output (often stdout or a file), but with custom set to (controller_global_t *).
+ *   - error:   The output file for error output messages, but with custom set to (controller_global_t *).
+ *   - warning: The output file for warning output messages, but with custom set to (controller_global_t *).
+ *   - debug:   The output file for debug output messages, but with custom set to (controller_global_t *).
  */
 #ifndef _di_controller_global_t_
   typedef struct {
@@ -35,24 +36,24 @@ extern "C" {
     controller_program_t *program;
     controller_thread_t *thread;
 
-    fl_print_t message;
-    fl_print_t output;
-    fl_print_t error;
-    fl_print_t warning;
-    fl_print_t debug;
+    fl_print_t *message;
+    fl_print_t *output;
+    fl_print_t *error;
+    fl_print_t *warning;
+    fl_print_t *debug;
   } controller_global_t;
 
-  #define controller_global_t_initialize { 0, 0, 0 }
+  #define controller_global_t_initialize { 0, 0, 0, 0, 0, 0, 0, 0 }
 
-  #define macro_controller_global_t_initialize(main, program, thread) { \
+  #define macro_controller_global_t_initialize_1(main, program, thread, message, output, error, warning, debug) { \
     main, \
     program, \
     thread, \
-    fl_print_t_initialize, \
-    fl_print_t_initialize, \
-    macro_fl_print_t_initialize_error(), \
-    macro_fl_print_t_initialize_warning(), \
-    macro_fl_print_t_initialize_debug(), \
+    message, \
+    output, \
+    error, \
+    warning, \
+    debug, \
   }
 #endif // _di_controller_global_t_
 
index fa02e5304ac02c673bcc986e5e1893e0f8091707..f0ef03c5398e34fa02a171566c2e22ab98186dd7 100644 (file)
@@ -42,12 +42,9 @@ extern "C" {
  * path_pids: An array of paths representing PID files.
  * stack:     A stack used to represent dependencies as Rule ID's to avoid circular Rule dependencies (If Rule A waits on Rule B, then Rule B must not wait on Rule A).
  *
- * rule:  A copy of the rule actively being executed.
- * cache: The cache used by this Instance.
- *
- * main:    The associated main program data (of type controller_main_t).
- * program: The associated program data.
- * thread:  The associated thread data (of type f_thread_t).
+ * rule:   A copy of the rule actively being executed.
+ * cache:  The cache used by this Instance.
+ * global: The global data.
  */
 #ifndef _di_controller_instance_t_
   typedef struct {
@@ -71,13 +68,7 @@ extern "C" {
 
     controller_rule_t rule;
     controller_cache_t cache;
-
-    // @fixme change this to void *global where global is controller_global_t??
-    // @fixme each instance probably needs its own thread data and this likely needs to be backported if it do-able in a non-breaking-change manner!
-    // @fixme I may want to instead replace the controller_global_t with controller_instance_t during printing calls (maybe more??).
-    void *main;
-    controller_program_t *program;
-    void *thread;
+    controller_global_t global;
   } controller_instance_t;
 
   #define controller_instance_t_initialize { \
@@ -97,9 +88,7 @@ extern "C" {
     f_number_unsigneds_t_initialize, \
     controller_rule_t_initialize, \
     controller_cache_t_initialize, \
-    0, \
-    0, \
-    0, \
+    controller_global_t_initialize, \
   }
 #endif // _di_controller_instance_t_
 
index 2970a3cf3196f538f10750917f5585ad843bfaf4..918e668fecebd4e3272f42302719758682111a4a 100644 (file)
@@ -38,7 +38,7 @@ extern "C" {
  * cache:     A cache used by the main entry/rule processing thread for synchronous operations.
  */
 #ifndef _di_controller_thread_t_
-  typedef struct controller_thread_t_ {
+  typedef struct {
     uint8_t enabled;
     int signal;
     f_status_t status;
index b6a8ac2058eb112500d5320e455339e0f72e7040..7dce5c601ac27f89bc497e61faf96811f5dff2fb 100644 (file)
 #include <program/controller/main/common/type/lock.h>
 #include <program/controller/main/common/type/rule.h>
 #include <program/controller/main/common/type/program.h>
-#include <program/controller/main/common/type/instance.h>
 #include <program/controller/main/common/type/thread.h>
 #include <program/controller/main/common/type.h>
 #include <program/controller/main/common/type/global.h>
+#include <program/controller/main/common/type/instance.h>
 #include <program/controller/main/common.h>
 #include <program/controller/main/path.h>
 #include <program/controller/main/print/action.h>
 #include <program/controller/main/rule/setting.h>
 #include <program/controller/main/rule/validate.h>
 #include <program/controller/main/rule/wait.h>
+#include <program/controller/main/process.h>
 
 #ifdef __cplusplus
 extern "C" {
index f23c97542b55346d6f1a1421f981977e0ad1d4a7..8e69031f46c2b4c706ba6b7c6fb5305ba19e61e0 100644 (file)
@@ -18,6 +18,24 @@ extern "C" {
   }
 #endif // _di_controller_main_print_error_
 
+#ifndef _di_controller_main_print_error_failsafe_item_
+  f_status_t controller_main_print_error_failsafe_item(fl_print_t * const print, controller_thread_t * const thread, f_string_dynamic_t * const name) {
+
+    if (!print) return F_status_set_error(F_output_not);
+    if (print->verbosity < f_console_verbosity_error_e) return F_output_not;
+
+    controller_lock_print(print->to, thread);
+
+    fl_print_format("%r%[%QFailed while processing requested failsafe item '%]", print->to, f_string_eol_s, print->context, print->prefix, print->context);
+    fl_print_format(f_string_format_Q_single_s.string, print->to, print->set->notable, name, print->set->notable);
+    fl_print_format(f_string_format_sentence_end_quote_s.string, print->to, print->context, print->context, f_string_eol_s);
+
+    controller_unlock_print_flush(print->to, thread);
+
+    return F_okay;
+  }
+#endif // _di_controller_main_print_error_failsafe_item_
+
 #ifndef _di_controller_main_print_error_file_
   f_status_t controller_main_print_error_file(fl_print_t * const print, const f_string_t function, const f_string_static_t name, const f_string_static_t operation, const uint8_t type) {
 
@@ -44,6 +62,24 @@ extern "C" {
   }
 #endif // _di_controller_main_print_error_file_status_
 
+#ifndef _di_controller_main_print_error_file_pid_exists_
+  f_status_t controller_main_print_error_file_pid_exists(fl_print_t * const print, controller_thread_t * const thread, f_string_dynamic_t * const path) {
+
+    if (!print) return F_status_set_error(F_output_not);
+    if (print->verbosity < f_console_verbosity_error_e) return F_output_not;
+
+    controller_lock_print(print->to, 0);
+
+    fl_print_format("%r%[%QThe pid file '%]", print->to, f_string_eol_s, print->context, print->prefix, print->context);
+    fl_print_format(f_string_format_Q_single_s.string, print->to, print->set->notable, path, print->set->notable);
+    fl_print_format("%[' must not already exist.%]%r", print->to, print->context, print->context, f_string_eol_s);
+
+    controller_unlock_print_flush(print->to, 0);
+
+    return F_okay;
+  }
+#endif // _di_controller_main_print_error_file_pid_exists_
+
 #ifndef _di_controller_main_print_error_status_
   f_status_t controller_main_print_error_status(fl_print_t * const print, const f_string_t function, const f_status_t status) {
 
index 61fe780b4911cc88740aed951172074f59e890fd..46282b918b0fb0c99359a7b3aa9adccd59c1e6c3 100644 (file)
@@ -40,6 +40,36 @@ extern "C" {
 #endif // _di_controller_main_print_error_
 
 /**
+ * Print error message regarding the failsafe item failing.
+ *
+ * @param print
+ *   The output structure to print to.
+ *
+ *   Must not be NULL.
+ *
+ *   This does not alter print.custom.setting.state.status.
+ * @param thread
+ *   The name of the function associated with the error.
+ *
+ *   Must not be NULL.
+ * @param name
+ *   The name of the item.
+ *
+ *   Must not be NULL.
+ *
+ * @return
+ *   F_okay on success.
+ *   F_output_not on success, but no printing is performed.
+ *
+ *   F_output_not (with error bit) if setting is NULL.
+ *
+ * @see fll_error_print()
+ */
+#ifndef _di_controller_main_print_error_failsafe_item_
+  extern f_status_t controller_main_print_error_failsafe_item(fl_print_t * const print, controller_thread_t * const thread, f_string_dynamic_t * const name);
+#endif // _di_controller_main_print_error_failsafe_item_
+
+/**
  * Print file related error or warning messages.
  *
  * @param print
@@ -185,6 +215,36 @@ extern "C" {
   extern void controller_main_print_rule_error_cache(fl_print_t * const print, const controller_cache_action_t cache, const bool item);
 #endif // _di_controller_main_print_rule_error_cache_
 
+/**
+ * Print error message regarding the pid file already existing.
+ *
+ * @param print
+ *   The output structure to print to.
+ *
+ *   Must not be NULL.
+ *
+ *   This does not alter print.custom.setting.state.status.
+ * @param thread
+ *   The name of the function associated with the error.
+ *
+ *   Must not be NULL.
+ * @param path
+ *   The path to the PID file.
+ *
+ *   Must not be NULL.
+ *
+ * @return
+ *   F_okay on success.
+ *   F_output_not on success, but no printing is performed.
+ *
+ *   F_output_not (with error bit) if setting is NULL.
+ *
+ * @see fll_error_print()
+ */
+#ifndef _di_controller_main_print_error_file_pid_exists_
+  extern f_status_t controller_main_print_error_file_pid_exists(fl_print_t * const print, controller_thread_t * const thread, f_string_dynamic_t * const path);
+#endif // _di_controller_main_print_error_file_pid_exists_
+
 #ifdef __cplusplus
 } // extern "C"
 #endif
diff --git a/sources/c/main/process.c b/sources/c/main/process.c
new file mode 100644 (file)
index 0000000..5c5bddd
--- /dev/null
@@ -0,0 +1,169 @@
+#include "controller.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef _di_controller_main_process_
+  void controller_main_process(controller_main_t * const main, controller_program_t * const program) {
+
+    if (!main || !program || F_status_is_error(main->setting.state.status)) return;
+
+    main->setting.state.status = F_okay;
+
+    if (main->setting.flag & controller_main_flag_version_copyright_help_e) {
+      if (main->setting.flag & controller_main_flag_help_e) {
+        controller_main_print_message_help(&main->program.message, F_false);
+      }
+      else if (main->setting.flag & controller_main_flag_version_e) {
+        fll_program_print_version(&main->program.message, controller_program_version_s);
+      }
+      else if (main->setting.flag & controller_main_flag_copyright_e) {
+        fll_program_print_copyright(&main->program.message, fll_program_copyright_year_author_s);
+      }
+
+      return;
+    }
+
+    // A custom status at this scope is used to prevent the status from being changed by any child process or sub-function.
+    f_status_t status = F_okay;
+
+    fl_print_t message = main->program.message;
+    fl_print_t output = main->program.output;
+    fl_print_t error = main->program.error;
+    fl_print_t warning = main->program.warning;
+    fl_print_t debug = main->program.debug;
+
+    controller_thread_t thread = controller_thread_t_initialize;
+    controller_global_t global = macro_controller_global_t_initialize_1(main, program, &thread, &message, &output, &error, &warning, &debug);
+
+    message.custom = output.custom = error.custom = warning.custom = debug.custom = (void *) &global;
+
+    // The global locks must be initialized, but only once, so initialize immediately upon allocation.
+    status = controller_main_lock_create(&thread.lock);
+
+    if (F_status_is_error(status)) {
+      controller_main_print_error_status(&main->program.error, macro_controller_f(controller_main_lock_create), status);
+    } else {
+      status = f_memory_array_increase(controller_allocation_small_d, sizeof(controller_instance_t), (void **) &thread.instances.array, &thread.instances.used, &thread.instances.size);
+
+      if (F_status_is_error(status)) {
+        controller_main_print_error_status(&main->program.error, macro_controller_f(f_memory_array_increase), status);
+      }
+    }
+
+    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;
+
+      controller_main_print_error_status(&main->program.error, macro_controller_f(f_thread_create), status);
+    }
+    else {
+      if (main->setting.flag & controller_main_flag_daemon_e) {
+        program->ready = controller_setting_ready_done_e;
+
+        if (f_file_exists(program->path_pid, F_true) == F_true) {
+          status = F_status_set_error(F_available_not);
+          program->ready = controller_setting_ready_abort_e;
+
+          controller_main_print_error_file_pid_exists(&main->program.error, &thread, program->path_pid);
+        }
+      }
+      else if (program->name_entry.used) {
+        status = f_thread_create(0, &thread.id_entry, &controller_main_thread_entry, (void *) &global);
+
+        if (F_status_is_error(status)) {
+          controller_main_print_error_status(&main->program.error, macro_controller_f(f_thread_create), status);
+        }
+        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 are complete.
+    if (F_status_is_error_not(status) && status != F_failure && status != F_child && thread.enabled == controller_thread_enabled_e) {
+      if (!(main->setting.flag & controller_main_flag_validate_e)) {
+
+        // Wait for the entry thread to complete before starting the rule thread.
+        controller_main_thread_join(&thread.id_rule);
+
+        if (thread.enabled && program->mode == controller_setting_mode_service_e) {
+          status = f_thread_create(0, &thread.id_rule, &controller_main_thread_rule, (void *) &global);
+
+          if (F_status_is_error(status)) {
+            thread.id_rule = 0;
+          }
+          else {
+            status = f_thread_create(0, &thread.id_cleanup, &controller_main_thread_cleanup, (void *) &global);
+          }
+
+          if (F_status_is_error(status)) {
+            thread.id_cleanup = 0;
+
+            controller_main_print_error_status(&main->program.error, macro_controller_f(f_thread_create), status);
+          }
+        }
+      }
+    }
+
+    if (status == F_child) {
+      main->setting.state.status = F_child;
+
+      controller_main_thread_delete(&thread);
+
+      return;
+    }
+
+    if (F_status_is_error_not(status) && status != F_failure && !(main->setting.flag & controller_main_flag_validate_e) && controller_thread_is_enabled(F_true, &thread)) {
+      if (program->mode == controller_setting_mode_service_e) {
+        controller_main_thread_join(&thread.id_signal);
+      }
+      else if (program->mode == controller_setting_mode_helper_e || program->mode == controller_setting_mode_program_e) {
+        status = controller_main_rule_wait_all(global, F_true, F_false);
+      }
+    }
+
+    controller_main_thread_process_cancel(global, F_true, controller_thread_cancel_call_e);
+
+    controller_main_thread_process_exit(&global);
+
+    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_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_entry = 0;
+    thread.id_rule = 0;
+    thread.id_signal = 0;
+
+    controller_main_thread_delete(&thread);
+
+    if (F_status_set_fine(status) == F_interrupt) {
+      fll_program_print_signal_received(&main->program.warning, thread.signal);
+
+      if (main->program.output.verbosity > f_console_verbosity_quiet_e) {
+        fll_print_dynamic_raw(f_string_eol_s, main->program.output.to);
+      }
+
+      fll_program_print_signal_received(&main->program.warning, main->program.signal_received);
+
+      return F_status_set_error(F_interrupt);
+    }
+
+    return F_status_is_error(status) ? F_status_set_error(F_failure) : F_okay;
+  }
+#endif // _di_controller_main_process_
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
diff --git a/sources/c/main/process.h b/sources/c/main/process.h
new file mode 100644 (file)
index 0000000..e800d40
--- /dev/null
@@ -0,0 +1,55 @@
+/**
+ * FLL - Level 3
+ *
+ * Project: Controller
+ * API Version: 0.7
+ * Licenses: lgpl-2.1-or-later
+ *
+ * Provides the main process functionality.
+ *
+ * This is auto-included and should not need to be explicitly included.
+ */
+#ifndef _controller_main_process_h
+#define _controller_main_process_h
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Execute main program process.
+ *
+ * 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 and settings.
+ *
+ *   Must not be NULL.
+ *
+ *   This alters main.setting.state.status:
+ *     F_okay on success.
+ *     F_true on success when performing verification and verify passed.
+ *     F_false on success when performing verification and verify failed.
+ *     F_interrupt on (exit) signal received.
+ *
+ *     F_parameter (with error bit) if main is NULL or setting is NULL.
+ * @param program
+ *   A pointer to the current program settings.
+ *
+ *   Must not be NULL.
+ */
+#ifndef controller_main_process
+  extern void controller_main_process(controller_main_t * const main, controller_program_t * const program);
+#endif // controller_main_process
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // _controller_main_process_h
index c9705306163d0efc3107b8d894088f39cd7dc1a8..4fcd809f157503076b6837cf3d8d720e01959808 100644 (file)
@@ -11,94 +11,76 @@ extern "C" {
 
     f_thread_cancel_state_set(PTHREAD_CANCEL_DEFERRED, 0);
 
-    controller_instance_t * const instance = (controller_instance_t * const ) arguments;
+    controller_global_t * const global = (controller_global_t * const) arguments;
 
-    if (!controller_main_thread_is_enabled(F_true, instance->thread)) return 0;
+    if (!controller_main_thread_is_enabled(F_true, global->thread)) return 0;
 
-    controller_main_t * const main = instance->main;
-    controller_cache_t * const cache = &instance->thread->cache;
-    f_status_t * const status = &instance->thread->status;
+    f_status_t * const status = &global->thread->status;
 
-    *status = controller_entry_read(*instance->global, F_true, cache);
+    *status = controller_main_entry_read(global, F_true);
 
     if (F_status_set_fine(*status) == F_interrupt) {
-      instance->setting->ready = controller_setting_ready_abort_e;
+      global->program->ready = controller_setting_ready_abort_e;
     }
     else if (F_status_is_error(*status)) {
-      instance->setting->ready = controller_setting_ready_fail_e;
+      global->program->ready = controller_setting_ready_fail_e;
     }
     else if (*status != F_child) {
-      *status = controller_entry_preprocess(*instance->global, F_true, cache);
+      *status = controller_main_entry_preprocess(global, F_true);
 
-      if ((main->program.parameters.array[controller_parameter_simulate_e].result & f_console_result_found_e) && (main->program.parameters.array[controller_parameter_validate_e].result & f_console_result_found_e)) {
-        controller_entry_setting_validate(*instance->global, F_true, cache);
+      if ((global->main->program.parameters.array[controller_parameter_simulate_e].result & f_console_result_found_e) && (global->main->program.parameters.array[controller_parameter_validate_e].result & f_console_result_found_e)) {
+        controller_main_entry_setting_validate(global, F_true);
       }
     }
 
     if (F_status_is_error_not(*status) && *status != F_child) {
-      if (!(main->program.parameters.array[controller_parameter_validate_e].result & f_console_result_found_e) || (main->program.parameters.array[controller_parameter_simulate_e].result & f_console_result_found_e)) {
-
-        if (instance->setting->instance.pid == controller_entry_pid_require_e && f_file_exists(instance->setting->path_pid, F_true) == F_true) {
-          if (main->program.error.verbosity > f_console_verbosity_quiet_e) {
-            controller_lock_print(main->program.error.to, instance->thread);
-
-            fl_print_format("%r%[%QThe pid file '%]", main->program.error.to, f_string_eol_s, main->program.error.context, main->program.error.prefix, main->program.error.context);
-            fl_print_format(f_string_format_Q_single_s.string, main->program.error.to, main->program.error.notable, instance->setting->path_pid, main->program.error.notable);
-            fl_print_format("%[' must not already exist.%]%r", main->program.error.to, main->program.error.context, main->program.error.context, f_string_eol_s);
-
-            controller_unlock_print_flush(main->program.error.to, instance->thread);
-          }
+      if (!(global->main->program.parameters.array[controller_parameter_validate_e].result & f_console_result_found_e) || (global->main->program.parameters.array[controller_parameter_simulate_e].result & f_console_result_found_e)) {
 
-          instance->setting->ready = controller_setting_ready_fail_e;
+        if (global->program->entry.pid == controller_entry_pid_require_e && f_file_exists(global->program->path_pid, F_true) == F_true) {
           *status = F_status_set_error(F_available_not);
+          global->program->ready = controller_setting_ready_fail_e;
+
+          controller_main_print_error_file_pid_exists(&global->main->program.error, global->thread, global->program->path_pid);
         }
         else {
-          *status = controller_entry_process(instance->global, cache, F_false, F_true);
+          *status = controller_main_entry_process(global, F_false, F_true);
 
           if (F_status_is_error(*status)) {
-            instance->setting->ready = controller_setting_ready_fail_e;
+            global->program->ready = controller_setting_ready_fail_e;
 
-            if ((F_status_set_fine(*status) == F_execute || F_status_set_fine(*status) == F_require) && (instance->setting->flag & controller_setting_flag_failsafe_e)) {
-              const uint8_t original_enabled = instance->thread->enabled;
+            if ((F_status_set_fine(*status) == F_execute || F_status_set_fine(*status) == F_require) && (program->flag & controller_setting_flag_failsafe_e)) {
+              const uint8_t original_enabled = global->thread->enabled;
 
               // Restore operating mode so that the failsafe can execute.
-              *status = f_thread_mutex_lock(&instance->thread->lock.alert);
+              *status = f_thread_mutex_lock(&global->thread->lock.alert);
 
               if (F_status_is_error_not(*status)) {
-                instance->thread->enabled = controller_thread_enabled_e;
+                global->thread->enabled = controller_thread_enabled_e;
 
-                f_thread_mutex_unlock(&instance->thread->lock.alert);
+                f_thread_mutex_unlock(&global->thread->lock.alert);
               }
 
-              // Restart the signal thread to allow for signals while operating the failsafe Items.
-              if (!instance->thread->id_signal) {
-                f_thread_create(0, &instance->thread->id_signal, &controller_main_thread_signal_normal, (void *) instance->global);
+              // Restart the signal global->thread to allow for signals while operating the failsafe Items.
+              if (!global->thread->id_signal) {
+                f_thread_create(0, &global->thread->id_signal, &controller_main_thread_signal_normal, (void *) global);
               }
 
-              const f_status_t status_failsafe = controller_entry_process(instance->global, cache, F_true, F_true);
+              const f_status_t status_failsafe = controller_main_entry_process(global, F_true, F_true);
 
               if (F_status_is_error(status_failsafe)) {
-                if (main->program.error.verbosity > f_console_verbosity_quiet_e) {
-                  controller_lock_print(main->program.error.to, instance->thread);
-
-                  fl_print_format("%r%[%QFailed while processing requested failsafe item '%]", main->program.error.to, f_string_eol_s, main->program.error.context, main->program.error.prefix, main->program.error.context);
-                  fl_print_format(f_string_format_Q_single_s.string, main->program.error.to, main->program.error.notable, instance->setting->instance.items.array[instance->setting->failsafe_item_id].name, main->program.error.notable);
-                  fl_print_format(f_string_format_sentence_end_quote_s.string, main->program.error.to, main->program.error.context, main->program.error.context, f_string_eol_s);
-
-                  controller_unlock_print_flush(main->program.error.to, instance->thread);
-                }
-
                 *status = F_status_set_error(F_failure);
+
+                controller_main_print_error_failsafe_item(&global->main->program.error, global->thread, global->program->entry.items.array[program->failsafe_item_id].name);
               }
               else {
 
                 // Restore operating mode to value prior to failsafe mode.
-                *status = f_thread_mutex_lock(&instance->thread->lock.alert);
+                *status = f_thread_mutex_lock(&global->thread->lock.alert);
 
                 if (F_status_is_error_not(*status)) {
-                  instance->thread->enabled = original_enabled;
+                  global->thread->enabled = original_enabled;
 
-                  f_thread_mutex_unlock(&instance->thread->lock.alert);
+                  f_thread_mutex_unlock(&global->thread->lock.alert);
                 }
 
                 *status = F_failure;
@@ -106,21 +88,21 @@ extern "C" {
             }
           }
           else if (F_status_set_fine(*status) == F_interrupt) {
-            instance->setting->ready = controller_setting_ready_abort_e;
+            global->program->ready = controller_setting_ready_abort_e;
           }
           else if (*status != F_child) {
-            instance->setting->ready = controller_setting_ready_done_e;
+            global->program->ready = controller_setting_ready_done_e;
           }
         }
 
-        if (F_status_is_error_not(*status) && *status != F_child && main->program.parameters.array[controller_parameter_validate_e].result == f_console_result_none_e && instance->setting->mode == controller_setting_mode_helper_e) {
+        if (F_status_is_error_not(*status) && *status != F_child && global->main->program.parameters.array[controller_parameter_validate_e].result == f_console_result_none_e && global->program->mode == controller_setting_mode_helper_e) {
           f_time_spec_t time;
           time.tv_sec = controller_main_thread_exit_helper_timeout_seconds_d;
           time.tv_nsec = controller_main_thread_exit_helper_timeout_nanoseconds_d;
 
           nanosleep(&time, 0);
 
-          controller_main_thread_process_cancel(*(instance->global), F_true, controller_thread_cancel_exit_e);
+          controller_main_thread_process_cancel(global, F_true, controller_thread_cancel_exit_e);
         }
       }
     }
@@ -129,17 +111,17 @@ extern "C" {
 
       // 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.
-      controller_thread_delete_simple(instance->thread);
-      controller_process_delete(instance->setting);
-      controller_main_delete(main);
+      controller_main_thread_delete(global->thread);
+      controller_main_program_delete(global->program);
+      controller_main_delete(global->main);
 
-      // According to the manpages, pthread_exit() calls exit(0), which the value of main->program.child should be returned instead.
-      if (main->program.child) exit(main->program.child);
+      // According to the manpages, pthread_exit() calls exit(0), which the value of global->main->program.child should be returned instead.
+      if (global->main->program.child) exit(global->main->program.child);
 
       return 0;
     }
 
-    f_thread_condition_signal_all(&instance->thread->lock.alert_condition);
+    f_thread_condition_signal_all(&global->thread->lock.alert_condition);
 
     return 0;
   }
@@ -152,83 +134,75 @@ extern "C" {
 
     f_thread_cancel_state_set(PTHREAD_CANCEL_DEFERRED, 0);
 
-    controller_main_entry_t *instance = (controller_main_entry_t *) arguments;
+    controller_global_t * const global = (controller_global_t * const) arguments;
 
-    controller_main_t * const main = instance->main;
-    controller_cache_t * const cache = &instance->thread->cache;
-    f_status_t * const status = &instance->thread->status;
+    controller_main_t * const main = global->main;
+    controller_cache_t * const cache = &global->thread->cache;
+    f_status_t * const status = &global->thread->status;
 
-    *status = controller_entry_read(*instance->global, F_false, cache);
+    *status = controller_entry_read(global, F_false);
 
     if (F_status_set_fine(*status) == F_interrupt) {
-      instance->setting->ready = controller_setting_ready_abort_e;
+      global->program->ready = controller_setting_ready_abort_e;
     }
     else if (F_status_is_error(*status)) {
-      instance->setting->ready = controller_setting_ready_fail_e;
+      global->program->ready = controller_setting_ready_fail_e;
     }
     else if (*status == F_file_found_not) {
-      instance->setting->ready = controller_setting_ready_done_e;
+      global->program->ready = controller_setting_ready_done_e;
     }
     else if (*status != F_child) {
-      *status = controller_entry_preprocess(*instance->global, F_false, cache);
+      *status = controller_entry_preprocess(global, F_false, cache);
 
       if ((main->program.parameters.array[controller_parameter_simulate_e].result & f_console_result_found_e) && (main->program.parameters.array[controller_parameter_validate_e].result & f_console_result_found_e)) {
-        controller_entry_setting_validate(*instance->global, F_false, cache);
+        controller_entry_setting_validate(global, F_false, cache);
       }
     }
 
     if (F_status_is_error_not(*status) && *status != F_child && *status != F_file_found_not) {
       if (!(main->program.parameters.array[controller_parameter_validate_e].result & f_console_result_found_e) || (main->program.parameters.array[controller_parameter_simulate_e].result & f_console_result_found_e)) {
 
-        *status = controller_entry_process(instance->global, cache, F_false, F_false);
+        *status = controller_entry_process(global, F_false, F_false);
 
         if (F_status_is_error(*status)) {
-          instance->setting->ready = controller_setting_ready_fail_e;
+          global->program->ready = controller_setting_ready_fail_e;
 
-          if ((F_status_set_fine(*status) == F_execute || F_status_set_fine(*status) == F_require) && (instance->setting->flag & controller_setting_flag_failsafe_e)) {
+          if ((F_status_set_fine(*status) == F_execute || F_status_set_fine(*status) == F_require) && (global->program->flag & controller_setting_flag_failsafe_e)) {
 
-            const uint8_t original_enabled = instance->thread->enabled;
+            const uint8_t original_enabled = 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(&instance->thread->lock.alert);
+              *status = f_thread_mutex_lock(&global->thread->lock.alert);
 
               if (F_status_is_error_not(*status)) {
-                instance->thread->enabled = controller_thread_enabled_exit_e;
+                global->thread->enabled = controller_thread_enabled_exit_e;
 
-                f_thread_mutex_unlock(&instance->thread->lock.alert);
+                f_thread_mutex_unlock(&global->thread->lock.alert);
               }
 
               // Restart the signal thread to allow for signals while operating the failsafe Items.
-              if (!instance->thread->id_signal) {
-                f_thread_create(0, &instance->thread->id_signal, &controller_main_thread_signal_other, (void *) instance->global);
+              if (!global->thread->id_signal) {
+                f_thread_create(0, &global->thread->id_signal, &controller_main_thread_signal_other, (void *) global);
               }
             }
 
-            const f_status_t status_failsafe = controller_entry_process(instance->global, cache, F_true, F_false);
+            const f_status_t status_failsafe = controller_entry_process(global, F_true, F_false);
 
             if (F_status_is_error(status_failsafe)) {
-              if (main->program.error.verbosity > f_console_verbosity_quiet_e) {
-                controller_lock_print(main->program.error.to, instance->thread);
-
-                fl_print_format("%r%[%QFailed while processing requested failsafe item '%]", main->program.error.to, f_string_eol_s, main->program.error.context, main->program.error.prefix, main->program.error.context);
-                fl_print_format(f_string_format_Q_single_s.string, main->program.error.to, main->program.error.notable, instance->setting->instance.items.array[instance->setting->failsafe_item_id].name, main->program.error.notable);
-                fl_print_format(f_string_format_sentence_end_quote_s.string, main->program.error.to, main->program.error.context, main->program.error.context, f_string_eol_s);
-
-                controller_unlock_print_flush(main->program.error.to, instance->thread);
-              }
-
               *status = F_status_set_error(F_failure);
+
+              controller_main_print_error_failsafe_item(&global->main->program.error, global->thread, global->program->entry.items.array[program->failsafe_item_id].name);
             }
             else {
 
               // Restore operating mode to value prior to failsafe mode.
-              *status = f_thread_mutex_lock(&instance->thread->lock.alert);
+              *status = f_thread_mutex_lock(&global->thread->lock.alert);
 
               if (F_status_is_error_not(*status)) {
-                instance->thread->enabled = original_enabled;
+                global->thread->enabled = original_enabled;
 
-                f_thread_mutex_unlock(&instance->thread->lock.alert);
+                f_thread_mutex_unlock(&global->thread->lock.alert);
               }
 
               *status = F_failure;
@@ -236,10 +210,10 @@ extern "C" {
           }
         }
         else if (F_status_set_fine(*status) == F_interrupt) {
-          instance->setting->ready = controller_setting_ready_abort_e;
+          global->program->ready = controller_setting_ready_abort_e;
         }
         else if (*status != F_child) {
-          instance->setting->ready = controller_setting_ready_done_e;
+          global->program->ready = controller_setting_ready_done_e;
         }
       }
     }
@@ -248,20 +222,20 @@ extern "C" {
 
       // 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.
-      controller_thread_delete_simple(instance->thread);
-      controller_process_delete(instance->setting);
+      controller_thread_delete_simple(global->thread);
+      controller_process_delete(global->program);
       controller_main_delete(main);
 
       return 0;
     }
 
-    if (F_status_is_error_not(f_thread_mutex_lock(&instance->thread->lock.alert))) {
-      instance->thread->enabled = controller_thread_enabled_not_e;
+    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(&instance->thread->lock.alert);
+      f_thread_mutex_unlock(&global->thread->lock.alert);
     }
 
-    f_thread_condition_signal_all(&instance->thread->lock.alert_condition);
+    f_thread_condition_signal_all(&global->thread->lock.alert_condition);
 
     return 0;
   }