]> Kevux Git Server - fll/commitdiff
Progress: controller program, add exit support.
authorKevin Day <thekevinday@gmail.com>
Tue, 20 Apr 2021 23:16:45 +0000 (18:16 -0500)
committerKevin Day <thekevinday@gmail.com>
Tue, 20 Apr 2021 23:29:18 +0000 (18:29 -0500)
This implements the initial work needed to get exit files working.

A new thread is added to handle both "entry" and "exit" to free up the "rule" thread and allow for the "exit" to start will the "rule" thread exists.

The thread enabled process is now more complex given that there needs to be stages so that "exit" threads can work while "entry" or "control" operations begin terminating.
Add several helper functions to help simplify the detection.

Add an "alert" lock to send/receive alerts.
This is a more general purpose lock that tells something waiting on the alert to wake up and see if anything you care about changed.

The file loading now needs to be aware of "optional" files, namely the exit files.
If the named exit file does not exist, then do not care as it is not required (unlike entry and rule files).

Many of the read and write locks are now timed so that they can check to see if the controller is enabled or not.

While working on this I noticed some oversights and things that I did not previously consider.
I need to follow this up with a redesign in the entry/exit rule handling.

Instead of "rule", I should have the rule actions like "start" and "stop".

There needs to be support for default behaviors, such as allow for "stop" to not have a parameter and to correctly find and terminate a service.

There needs to be consideration on dependencies when exiting, perhaps there needs to be an exit-specific dependency management.

21 files changed:
level_3/controller/c/controller.c
level_3/controller/c/controller.h
level_3/controller/c/private-common.c
level_3/controller/c/private-common.h
level_3/controller/c/private-controller.c
level_3/controller/c/private-controller.h
level_3/controller/c/private-entry.c
level_3/controller/c/private-entry.h
level_3/controller/c/private-rule.c
level_3/controller/c/private-rule.h
level_3/controller/c/private-thread.c
level_3/controller/c/private-thread.h
level_3/controller/data/settings/example/exits/serial.exit [new file with mode: 0644]
level_3/controller/data/settings/example/exits/sshd.exit [new file with mode: 0644]
level_3/controller/data/settings/example/rules/serial/s_1.rule
level_3/controller/data/settings/example/rules/serial/s_2.rule
level_3/controller/data/settings/example/rules/serial/s_3.rule
level_3/controller/data/settings/example/rules/serial/s_4.rule
level_3/controller/data/settings/example/rules/serial/s_5.rule
level_3/controller/data/settings/example/rules/serial/s_6.rule
level_3/controller/data/settings/example/rules/service/sshd.rule

index 3d25bd02876d25f732e86fb32daacfda282545b3..1a112b5b8b92979cf7ea02431d897d8296a34f9e 100644 (file)
@@ -91,7 +91,7 @@ extern "C" {
             fprintf(data->error.to.stream, "%c", f_string_eol_s[0]);
           }
 
-          controller_delete_data(data);
+          controller_data_delete(data);
           return F_status_set_error(status);
         }
       }
@@ -105,7 +105,7 @@ extern "C" {
         status = f_console_parameter_prioritize_right(parameters, choices, &choice);
 
         if (F_status_is_error(status)) {
-          controller_delete_data(data);
+          controller_data_delete(data);
           return status;
         }
 
@@ -133,30 +133,40 @@ extern "C" {
     if (data->parameters[controller_parameter_help].result == f_console_result_found) {
       controller_print_help(data->output, data->context);
 
-      controller_delete_data(data);
+      controller_data_delete(data);
       return F_none;
     }
 
     if (data->parameters[controller_parameter_version].result == f_console_result_found) {
       fll_program_print_version(data->output, controller_version);
 
-      controller_delete_data(data);
+      controller_data_delete(data);
       return F_none;
     }
 
     controller_setting_t setting = controller_setting_t_initialize;
 
-    f_string_static_t entry_name = f_string_static_t_initialize;
-
     if (data->remaining.used) {
-      entry_name.string = arguments.argv[data->remaining.array[0]];
-      entry_name.used = strnlen(entry_name.string, f_console_parameter_size);
-      entry_name.size = entry_name.used;
+      status = f_string_append_nulless(arguments.argv[data->remaining.array[0]], strnlen(arguments.argv[data->remaining.array[0]], f_console_parameter_size), &setting.name_entry);
     }
     else {
-      entry_name.string = controller_string_default_s;
-      entry_name.used = controller_string_default_length;
-      entry_name.size = entry_name.used;
+      status = f_string_append_nulless(controller_string_default_s, controller_string_default_length, &setting.name_entry);
+    }
+
+    if (F_status_is_error(status)) {
+      fll_error_print(data->error, F_status_set_fine(status), "f_string_append_nulless", F_true);
+
+      controller_data_delete(data);
+      return status;
+    }
+
+    status = f_string_dynamic_terminate_after(&setting.name_entry);
+
+    if (F_status_is_error(status)) {
+      fll_error_print(data->error, F_status_set_fine(status), "f_string_dynamic_terminate_after", F_true);
+
+      controller_data_delete(data);
+      return status;
     }
 
     if (data->parameters[controller_parameter_settings].result == f_console_result_found) {
@@ -227,7 +237,7 @@ extern "C" {
       status = f_string_append(controller_path_pid, controller_path_pid_length, &setting.path_pid);
 
       if (F_status_is_error_not(status)) {
-        status = f_string_append(entry_name.string, entry_name.used, &setting.path_pid);
+        status = f_string_append(setting.name_entry.string, setting.name_entry.used, &setting.path_pid);
       }
 
       if (F_status_is_error_not(status)) {
@@ -356,7 +366,7 @@ extern "C" {
     }
 
     if (F_status_is_error_not(status)) {
-      status = controller_thread_main(entry_name, data, &setting);
+      status = controller_thread_main(data, &setting);
     }
 
     // ensure a newline is always put at the end of the program execution, unless in quiet mode.
@@ -382,7 +392,7 @@ extern "C" {
     }
 
     controller_setting_delete_simple(&setting);
-    controller_delete_data(data);
+    controller_data_delete(data);
 
     if (status == F_child || status == F_signal) {
       return status;
@@ -392,8 +402,8 @@ extern "C" {
   }
 #endif // _di_controller_main_
 
-#ifndef _di_controller_delete_data_
-  f_status_t controller_delete_data(controller_data_t *data) {
+#ifndef _di_controller_data_delete_
+  f_status_t controller_data_delete(controller_data_t *data) {
 
     for (f_array_length_t i = 0; i < controller_total_parameters; i++) {
       f_macro_array_lengths_t_delete_simple(data->parameters[i].locations);
@@ -406,7 +416,7 @@ extern "C" {
 
     return F_none;
   }
-#endif // _di_controller_delete_data_
+#endif // _di_controller_data_delete_
 
 #ifdef __cplusplus
 } // extern "C"
index 417fb14cbbf259988af7963c36e50aa5ad68ff51..34e37b3ef9a8401d3b57fbbd6ce5757c5a194b55 100644 (file)
@@ -221,7 +221,7 @@ extern "C" {
 /**
  * Execute main program.
  *
- * Be sure to call controller_delete_data() after executing this.
+ * Be sure to call controller_data_delete() after executing this.
  *
  * @param arguments
  *   The parameters passed to the process.
@@ -233,7 +233,7 @@ extern "C" {
  *
  *   Status codes (with error bit) are returned on any problem.
  *
- * @see controller_delete_data()
+ * @see controller_data_delete()
  */
 #ifndef _di_controller_main_
   extern f_status_t controller_main(const f_console_arguments_t arguments, controller_data_t *data);
@@ -254,9 +254,9 @@ extern "C" {
  *
  * @see controller_main()
  */
-#ifndef _di_controller_delete_data_
-  extern f_status_t controller_delete_data(controller_data_t *data);
-#endif // _di_controller_delete_data_
+#ifndef _di_controller_data_delete_
+  extern f_status_t controller_data_delete(controller_data_t *data);
+#endif // _di_controller_data_delete_
 
 #ifdef __cplusplus
 } // extern "C"
index 2e4b6c24102fa8bbf5690b1cc5c7fed8393379c3..959d22ab5846c9ab9f54929c01ef747d8112906b 100644 (file)
@@ -1,5 +1,6 @@
 #include "controller.h"
 #include "private-common.h"
+#include "private-thread.h"
 
 #ifdef __cplusplus
 extern "C" {
@@ -129,12 +130,18 @@ extern "C" {
     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_
@@ -179,9 +186,12 @@ extern "C" {
   void controller_lock_delete_simple(controller_lock_t *lock) {
 
     controller_lock_delete_mutex(&lock->print);
+    controller_lock_delete_mutex(&lock->alert);
 
     controller_lock_delete_rw(&lock->process);
     controller_lock_delete_rw(&lock->rule);
+
+    f_thread_condition_delete(&lock->alert_condition);
   }
 #endif // _di_controller_lock_delete_simple_
 
@@ -222,7 +232,7 @@ extern "C" {
 #endif // _di_controller_lock_error_critical_print_
 
 #ifndef _di_controller_lock_read_
-  f_status_t controller_lock_read(controller_thread_t * const thread, f_thread_lock_t *lock) {
+  f_status_t controller_lock_read(const bool is_normal, controller_thread_t * const thread, f_thread_lock_t *lock) {
 
     struct timespec time;
 
@@ -230,12 +240,12 @@ extern "C" {
 
     for (;;) {
 
-      controller_time(0, controller_thread_lock_read_timeout, &time);
+      controller_time(controller_thread_lock_read_timeout_seconds, controller_thread_lock_read_timeout_nanoseconds, &time);
 
       status = f_thread_lock_read_timed(&time, lock);
 
       if (status == F_time) {
-        if (!thread->enabled) {
+        if (!controller_thread_is_enabled(is_normal, thread)) {
           return F_signal;
         }
       }
@@ -248,8 +258,22 @@ extern "C" {
   }
 #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, thread, lock);
+  }
+#endif // _di_controller_lock_read_process_type_
+
 #ifndef _di_controller_lock_write_
-  f_status_t controller_lock_write(controller_thread_t * const thread, f_thread_lock_t *lock) {
+  f_status_t controller_lock_write(const bool is_normal, controller_thread_t * const thread, f_thread_lock_t *lock) {
 
     struct timespec time;
 
@@ -257,12 +281,12 @@ extern "C" {
 
     for (;;) {
 
-      controller_time(0, controller_thread_lock_write_timeout, &time);
+      controller_time(controller_thread_lock_write_timeout_seconds, controller_thread_lock_write_timeout_nanoseconds, &time);
 
       status = f_thread_lock_write_timed(&time, lock);
 
       if (status == F_time) {
-        if (!thread->enabled) {
+        if (!controller_thread_is_enabled(is_normal, thread)) {
           return F_signal;
         }
       }
@@ -275,6 +299,20 @@ extern "C" {
   }
 #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, thread, lock);
+  }
+#endif // _di_controller_lock_write_process_type_
+
 #ifndef _di_controller_print_unlock_flush_
   void controller_print_unlock_flush(FILE * const stream, f_thread_mutex_t *mutex) {
 
@@ -315,7 +353,7 @@ extern "C" {
 #ifndef _di_controller_process_wait_
   f_status_t controller_process_wait(const controller_main_t main, controller_process_t *process) {
 
-    if (!main.thread->enabled) {
+    if (!controller_thread_is_enabled_process(process, main.thread)) {
       return F_signal;
     }
 
@@ -346,7 +384,7 @@ extern "C" {
 
       f_thread_mutex_unlock(&process->wait_lock);
 
-      if (!main.thread->enabled) {
+      if (!controller_thread_is_enabled_process(process, main.thread)) {
         return F_signal;
       }
 
@@ -354,7 +392,8 @@ extern "C" {
         break;
       }
 
-      status_lock = controller_lock_read(main.thread, &process->lock);
+      status_lock = controller_lock_read_process(process, main.thread, &process->lock);
+
       if (status_lock == F_signal || F_status_is_error(status_lock)) {
         controller_lock_error_critical_print(main.data->error, F_status_set_fine(status_lock), F_true, main.thread);
 
@@ -369,17 +408,14 @@ extern "C" {
       else if (status != F_time) {
 
         // move up the wait timer after a trigger was received.
-        if (count < controller_thread_wait_timeout_1_before) {
+        if (count < controller_thread_wait_timeout_2_before) {
           count = 0;
         }
-        else if (count < controller_thread_wait_timeout_2_before) {
-          count = controller_thread_wait_timeout_1_before;
-        }
         else if (count < controller_thread_wait_timeout_3_before) {
-          count = controller_thread_wait_timeout_2_before;
+          count = controller_thread_wait_timeout_1_before;
         }
         else {
-          count = controller_thread_wait_timeout_3_before;
+          count = controller_thread_wait_timeout_2_before;
         }
       }
 
@@ -389,7 +425,7 @@ extern "C" {
         count++;
       }
 
-    } while (status == F_time && main.thread->enabled);
+    } while (status == F_time && controller_thread_is_enabled_process(process, main.thread));
 
     return status;
   }
@@ -615,6 +651,8 @@ extern "C" {
     f_string_dynamic_resize(0, &setting->path_pid);
     f_string_dynamic_resize(0, &setting->path_setting);
 
+    f_string_dynamic_resize(0, &setting->name_entry);
+
     controller_entry_items_delete_simple(&setting->entry.items);
     controller_entry_items_delete_simple(&setting->exit.items);
     controller_rules_delete_simple(&setting->rules);
@@ -631,14 +669,14 @@ extern "C" {
 #endif // _di_controller_thread_delete_simple_
 
 #ifndef _di_controller_time_
-  void controller_time(const time_t seconds, const long nanos, struct timespec *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) + nanos;
+    time->tv_nsec = (now.tv_usec * 1000) + nanoseconds;
 
     // If tv_nsec is 1 second or greater, then increment seconds.
     if (time->tv_nsec >= 1000000000) {
index 883fae2b14412be2a33578b713e0dc898d99c631..34d0d222138868dd9ef98921f0a05e91b1fcb95e 100644 (file)
@@ -453,25 +453,33 @@ 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 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.
- * process: The process r/w lock.
- * rule:    The rule r/w lock.
+ * print:           The print mutex lock.
+ * alert:           The alert mutex lock for waking up on alerts.
+ * 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_lock_t process;
     f_thread_lock_t rule;
+
+    f_thread_condition_t alert_condition;
   } controller_lock_t;
 
   #define controller_lock_t_initialize { \
     f_thread_mutex_t_initialize, \
+    f_thread_mutex_t_initialize, \
     f_thread_lock_t_initialize, \
     f_thread_lock_t_initialize, \
+    f_thread_condition_t_initialize, \
   }
 #endif // _di_controller_mutex_t_
 
@@ -771,6 +779,11 @@ extern "C" {
  * - active: A process is actively using this, and is running asynchronously.
  * - done:   A process has finished running on this and there is a thread that needs to be cleaned up.
  *
+ * Process Types:
+ * - entry:   The process is started from an entry.
+ * - exit:    The process is started from an exit.
+ * - control: The process is started from a control operation.
+ *
  * id:           The ID of this process relative to the processes array.
  * status:       The last execution status of the process.
  * state:        The state of the process.
@@ -803,12 +816,19 @@ extern "C" {
     controller_process_state_done,
   };
 
+  enum {
+    controller_process_type_entry = 1,
+    controller_process_type_exit,
+    controller_process_type_control,
+  };
+
   typedef struct {
     f_array_length_t id;
 
     uint8_t state;
     uint8_t action;
     uint8_t options;
+    uint8_t type;
     pid_t child;
 
     f_thread_id_t id_thread;
@@ -835,6 +855,7 @@ extern "C" {
     0, \
     0, \
     0, \
+    0, \
     f_thread_id_t_initialize, \
     f_thread_lock_t_initialize, \
     f_thread_lock_t_initialize, \
@@ -996,7 +1017,10 @@ extern "C" {
 #endif // _di_controller_entry_items_t_
 
 /**
- * The Entry.
+ * The Entry or Exit.
+ *
+ * Entry and Exit files are essentially the same structure with minor differences in settings and behavior.
+ * The structure is identical and due to lacking any particularly good name to represent both "entry" or "exit", the name "entry" is being used for both.
  *
  * status: The overall status.
  * items:  The array of entry items.
@@ -1017,6 +1041,18 @@ extern "C" {
 /**
  * All setting data.
  *
+ * controller_setting_ready_*:
+ *   - no:    Entry/Exit is not ready.
+ *   - wait:  Entry/Exit has "ready" somewhere in the file but is not yet ready.
+ *   - yes:   Entry/Exit is now ready (Entry/Exit is still being processed).
+ *   - done:  Entry/Exit is ready and processing is complete.
+ *   - fail:  Entry/Exit processing failed.
+ *   - abort: Abort received before finished processing Entry/Exit.
+ *
+ * controller_setting_mode_*:
+ *   - program: Run as a program, exiting when finished prrocess entry (and any respective exit).
+ *   - service: Run as a service, listening for requests after processing entry.
+ *
  * interruptable:    TRUE if the program responds to interrupt signals, FALSE to block/ignore interrupt signals.
  * ready:            State representing if the settings are all loaded and is ready to run program operations.
  * timeout_kill:     The timeout to wait relating to using a kill signal.
@@ -1062,6 +1098,8 @@ extern "C" {
     f_string_dynamic_t path_pid;
     f_string_dynamic_t path_setting;
 
+    f_string_dynamic_t name_entry;
+
     controller_entry_t entry;
     controller_entry_t exit;
     controller_rules_t rules;
@@ -1080,6 +1118,7 @@ extern "C" {
     f_string_dynamic_t_initialize, \
     f_string_dynamic_t_initialize, \
     f_string_dynamic_t_initialize, \
+    f_string_dynamic_t_initialize, \
     controller_entry_t_initialize, \
     controller_entry_t_initialize, \
     controller_rules_t_initialize, \
@@ -1098,44 +1137,67 @@ extern "C" {
  * status:     A status used by the main entry/rule processing thread for synchronous operations.
  * id_cleanup: The thread ID representing the Cleanup Process.
  * id_control: The thread ID representing the Control Process.
- * id_rule:    The thread ID representing the Entry or Rule Process.
+ * id_entry:   The thread ID representing the Entry or Exit Process.
+ * id_rule:    The thread ID representing the Rule Process.
  * id_signal:  The thread ID representing the Signal Process.
  * lock:       A r/w lock for operating on this structure.
  * processs:   All Rule Process thread data.
  * cache:      A cache used by the main entry/rule processing thread for synchronous operations.
  */
 #ifndef _di_controller_thread_t_
-  #define controller_thread_cleanup_interval_long  3600 // 1 hour in seconds.
-  #define controller_thread_cleanup_interval_short 180  // 3 minutes in seconds.
-  #define controller_thread_exit_process_cancel_wait 600000000 // 0.6 seconds in nanoseconds.
-  #define controller_thread_exit_process_cancel_total 150 // 90 seconds in multiples of wait.
-  #define controller_thread_simulation_timeout 200000 // 0.2 seconds in microseconds.
+  #define controller_thread_cleanup_interval_long     3600      // 1 hour in seconds.
+  #define controller_thread_cleanup_interval_short    180       // 3 minutes in seconds.
+  #define controller_thread_exit_process_cancel_wait  600000000 // 0.6 seconds in nanoseconds.
+  #define controller_thread_exit_process_cancel_total 150       // 90 seconds in multiples of wait.
+  #define controller_thread_simulation_timeout        200000    // 0.2 seconds in microseconds.
+
+  #define controller_thread_signal_wait_timeout_seconds     70
+  #define controller_thread_signal_wait_timeout_nanoseconds 0
 
-  // read locks are more common, use longer waits to reduce the potentially CPU activity.
-  // write locks are less common, use shorter waits to increase potential response time.
-  #define controller_thread_lock_read_timeout 2 // seconds
-  #define controller_thread_lock_write_timeout 100000000 // 0.1 seconds in nanoseconds.
+  #define controller_thread_lock_read_timeout_seconds      3
+  #define controller_thread_lock_read_timeout_nanoseconds  0
+  #define controller_thread_lock_write_timeout_seconds     3
+  #define controller_thread_lock_write_timeout_nanoseconds 0
 
   #define controller_thread_wait_timeout_1_before 4
   #define controller_thread_wait_timeout_2_before 12
   #define controller_thread_wait_timeout_3_before 28
 
-  #define controller_thread_wait_timeout_1_seconds 0
-  #define controller_thread_wait_timeout_1_nanoseconds 20000000 // 0.02 seconds in nanoseconds.
-  #define controller_thread_wait_timeout_2_seconds 0
+  #define controller_thread_wait_timeout_1_seconds     0
+  #define controller_thread_wait_timeout_1_nanoseconds 20000000  // 0.02 seconds in nanoseconds.
+  #define controller_thread_wait_timeout_2_seconds     0
   #define controller_thread_wait_timeout_2_nanoseconds 200000000 // 0.2 seconds in nanoseconds.
-  #define controller_thread_wait_timeout_3_seconds 2
+  #define controller_thread_wait_timeout_3_seconds     2
   #define controller_thread_wait_timeout_3_nanoseconds 0
-  #define controller_thread_wait_timeout_4_seconds 20
+  #define controller_thread_wait_timeout_4_seconds     20
   #define controller_thread_wait_timeout_4_nanoseconds 0
 
+  #define controller_thread_exit_ready_timeout_seconds     0
+  #define controller_thread_exit_ready_timeout_nanoseconds 500000000 // 0.5 seconds in nanoseconds.
+
+  /**
+   * States for enabled, designating how to stop the process.
+   *
+   * controller_thread_enabled_not: the controller is no longer enabled, shut down and abort all work.
+   * controller_thread_enabled: the controller is operating normally.
+   * controller_thread_enabled_stop: the controller is shutting down, only process exit rules and stop actions.
+   * controller_thread_enabled_stop_ready: the controller is shutting down, only process exit rules and stop actions, and now ready to send termination signals.
+   */
+  enum {
+    controller_thread_enabled_not = 0,
+    controller_thread_enabled,
+    controller_thread_enabled_stop,
+    controller_thread_enabled_stop_ready,
+  };
+
   typedef struct {
-    bool enabled;
+    uint8_t enabled;
     int signal;
     f_status_t status;
 
     f_thread_id_t id_cleanup;
     f_thread_id_t id_control;
+    f_thread_id_t id_entry;
     f_thread_id_t id_rule;
     f_thread_id_t id_signal;
 
@@ -1145,13 +1207,14 @@ extern "C" {
   } controller_thread_t;
 
   #define controller_thread_t_initialize { \
-    F_true, \
+    controller_thread_enabled, \
     0, \
     F_none, \
     f_thread_id_t_initialize, \
     f_thread_id_t_initialize, \
     f_thread_id_t_initialize, \
     f_thread_id_t_initialize, \
+    f_thread_id_t_initialize, \
     controller_lock_t_initialize, \
     controller_processs_t_initialize, \
     controller_cache_t_initialize, \
@@ -1184,22 +1247,18 @@ extern "C" {
 /**
  * A wrapper used for passing a set of entry processing and execution related data.
  *
- * name:    A string representing the entry name.
  * main:    The main data.
  * setting: The setting data.
  */
 #ifndef _di_controller_main_entry_t_
   typedef struct {
-    const f_string_static_t *name;
-
     controller_main_t *main;
     controller_setting_t *setting;
   } controller_main_entry_t;
 
-  #define controller_main_entry_t_initialize { 0, 0, 0 }
+  #define controller_main_entry_t_initialize { 0, 0 }
 
-  #define controller_macro_main_entry_t_initialize(name, main, setting) { \
-    name, \
+  #define controller_macro_main_entry_t_initialize(main, setting) { \
     main, \
     setting, \
   }
@@ -1436,10 +1495,13 @@ extern "C" {
  *
  * Given a r/w lock, periodically check to see if main thread is disabled while waiting.
  *
- * @param lock
- *   The r/w lock to obtain a read lock on.
+ * @param is_normal
+ *   If TRUE, then process as if this is a normal operation (entry and control).
+ *   If FALSE, then process as if this is an exit operation.
  * @param 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.
@@ -1453,18 +1515,69 @@ extern "C" {
  * @see f_thread_lock_read_timed()
  */
 #ifndef _di_controller_lock_read_
-  extern f_status_t controller_lock_read(controller_thread_t * const thread, f_thread_lock_t *lock) f_gcc_attribute_visibility_internal;
+  extern f_status_t controller_lock_read(const bool is_normal, controller_thread_t * const thread, f_thread_lock_t *lock) f_gcc_attribute_visibility_internal;
 #endif // _di_controller_lock_read_
 
 /**
- * Wait to get a write lock.
+ * 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 write lock on.
+ *   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_gcc_attribute_visibility_internal;
+#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_gcc_attribute_visibility_internal;
+#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 is a normal operation (entry and control).
+ *   If FALSE, then process as if this is 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.
@@ -1478,10 +1591,58 @@ extern "C" {
  * @see f_thread_lock_write_timed()
  */
 #ifndef _di_controller_lock_write_
-  extern f_status_t controller_lock_write(controller_thread_t * const thread, f_thread_lock_t *lock) f_gcc_attribute_visibility_internal;
+  extern f_status_t controller_lock_write(const bool is_normal, controller_thread_t * const thread, f_thread_lock_t *lock) f_gcc_attribute_visibility_internal;
 #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_gcc_attribute_visibility_internal;
+#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_gcc_attribute_visibility_internal;
+#endif // _di_controller_lock_write_process_type_
+
+/**
  * Flush the stream buffer and then unlock the mutex.
  *
  * Weird behavior was observed when piping data from this program.
@@ -1756,13 +1917,13 @@ extern "C" {
  *
  * @param seconds
  *   The seconds to add to current time.
- * @param nanos
+ * @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 nanos, struct timespec *time) f_gcc_attribute_visibility_internal;
+  void controller_time(const time_t seconds, const long nanoseconds, struct timespec *time) f_gcc_attribute_visibility_internal;
 #endif // _di_controller_time_
 
 #ifdef __cplusplus
index 07a2cb57ec7d8eb1152c8be64221c2b9a29fbaae..e8a68630aecdb0df375ea72dd24c168ba3aa13fb 100644 (file)
@@ -23,7 +23,7 @@ extern "C" {
 #ifndef _di_controller_string_dynamic_append_terminated_
   f_status_t controller_string_dynamic_append_terminated(const f_string_static_t source, f_string_dynamic_t *destination) {
 
-    f_status_t status = f_string_dynamic_append(source, 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);
@@ -41,7 +41,8 @@ extern "C" {
 #endif // _di_controller_string_dynamic_partial_append_terminated_
 
 #ifndef _di_controller_file_load_
-  f_status_t controller_file_load(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_main_t main, controller_cache_t *cache) {
+  f_status_t controller_file_load(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_main_t main, controller_cache_t *cache) {
+
     f_status_t status = F_none;
     f_file_t file = f_file_t_initialize;
 
@@ -101,6 +102,12 @@ extern "C" {
     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 (main.data->error.verbosity != f_console_verbosity_quiet) {
         f_thread_mutex_lock(&main.thread->lock.print);
 
@@ -397,11 +404,11 @@ extern "C" {
 #endif // _di_controller_get_id_group_
 
 #ifndef _di_controller_perform_ready_
-  f_status_t controller_perform_ready(controller_main_t main, controller_cache_t *cache) {
+  f_status_t controller_perform_ready(const bool is_entry, controller_main_t main, controller_cache_t *cache) {
     f_status_t status = F_none;
 
     // only create pid file when not in validate mode.
-    if (main.data->parameters[controller_parameter_validate].result == f_console_result_none) {
+    if (is_entry && main.data->parameters[controller_parameter_validate].result == f_console_result_none) {
 
       status = controller_file_pid_create(main.data->pid, main.setting->path_pid);
 
@@ -445,7 +452,7 @@ extern "C" {
 #endif // _di_controller_perform_ready_
 
 #ifndef _di_controller_preprocess_entry_
-  f_status_t controller_preprocess_entry(controller_main_t main, controller_cache_t *cache) {
+  f_status_t controller_preprocess_entry(const bool is_entry, controller_main_t main, controller_cache_t *cache) {
     f_status_t status = F_none;
     f_status_t status2 = F_none;
 
@@ -455,10 +462,14 @@ extern "C" {
     f_array_length_t at_i = 0;
     f_array_length_t at_j = 1;
 
+    controller_entry_t *entry = is_entry ? &main.setting->entry : &main.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 main.setting->ready in this function may need mutex lock protection.
     main.setting->ready = controller_setting_ready_no;
 
     cache->ats.used = 0;
@@ -472,6 +483,7 @@ extern "C" {
 
     if (F_status_is_error(status)) {
       controller_entry_error_print(main.data->error, cache->action, F_status_set_fine(status), "f_macro_array_lengths_t_increase_by", F_true, main.thread);
+
       return status;
     }
 
@@ -480,10 +492,10 @@ extern "C" {
     cache->ats.array[1] = 0;
     cache->ats.used = 2;
 
-    cache->action.line_item = main.setting->entry.items.array[0].line;
+    cache->action.line_item = entry->items.array[0].line;
     cache->action.name_item.used = 0;
 
-    status = controller_string_dynamic_append_terminated(main.setting->entry.items.array[0].name, &cache->action.name_item);
+    status = controller_string_dynamic_append_terminated(entry->items.array[0].name, &cache->action.name_item);
 
     if (F_status_is_error(status)) {
       controller_entry_error_print(main.data->error, cache->action, F_status_set_fine(status), "controller_string_dynamic_append_terminated", F_true, main.thread);
@@ -491,11 +503,11 @@ extern "C" {
       return status;
     }
 
-    for (; main.thread->enabled; ) {
+    while (controller_thread_is_enabled(is_entry, main.thread)) {
 
-      actions = &main.setting->entry.items.array[cache->ats.array[at_i]].actions;
+      actions = &entry->items.array[cache->ats.array[at_i]].actions;
 
-      for (; cache->ats.array[at_j] < actions->used && main.thread->enabled; ++cache->ats.array[at_j]) {
+      for (; cache->ats.array[at_j] < actions->used && controller_thread_is_enabled(is_entry, main.thread); ++cache->ats.array[at_j]) {
 
         cache->action.line_action = actions->array[cache->ats.array[at_j]].line;
         cache->action.name_action.used = 0;
@@ -539,9 +551,9 @@ extern "C" {
           }
 
           // walk though each items and check to see if the item actually exists.
-          for (i = 1; i < main.setting->entry.items.used; ++i) {
+          for (i = 1; i < entry->items.used && controller_thread_is_enabled(is_entry, main.thread); ++i) {
 
-            if (fl_string_dynamic_compare(main.setting->entry.items.array[i].name, actions->array[cache->ats.array[at_j]].parameters.array[0]) == F_equal_to) {
+            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) {
@@ -552,7 +564,7 @@ extern "C" {
 
                     fprintf(main.data->error.to.stream, "%c", f_string_eol_s[0]);
                     fprintf(main.data->error.to.stream, "%s%sThe entry item named '", main.data->error.context.before->string, main.data->error.prefix ? main.data->error.prefix : f_string_empty_s);
-                    fprintf(main.data->error.to.stream, "%s%s%s%s", main.data->error.context.after->string, main.data->error.notable.before->string, main.setting->entry.items.array[i].name.string, main.data->error.notable.after->string);
+                    fprintf(main.data->error.to.stream, "%s%s%s%s", main.data->error.context.after->string, main.data->error.notable.before->string, entry->items.array[i].name.string, main.data->error.notable.after->string);
                     fprintf(main.data->error.to.stream, "%s' cannot be executed because recursion is not allowed.%s%c", main.data->error.context.before->string, main.data->error.context.after->string, f_string_eol_s[0]);
 
                     controller_entry_error_print_cache(main.data->error, cache->action);
@@ -594,9 +606,9 @@ extern "C" {
               cache->action.line_action = 0;
 
               cache->action.name_item.used = 0;
-              cache->action.line_item = main.setting->entry.items.array[i].line;
+              cache->action.line_item = entry->items.array[i].line;
 
-              status2 = controller_string_dynamic_append_terminated(main.setting->entry.items.array[i].name, &cache->action.name_item);
+              status2 = controller_string_dynamic_append_terminated(entry->items.array[i].name, &cache->action.name_item);
 
               if (F_status_is_error(status2)) {
                 controller_entry_error_print(main.data->error, cache->action, F_status_set_fine(status2), "controller_string_dynamic_append_terminated", F_true, main.thread);
@@ -608,8 +620,8 @@ extern "C" {
             }
           } // for
 
-          if (error_has || i >= main.setting->entry.items.used) {
-            if (i >= main.setting->entry.items.used) {
+          if (error_has || i >= entry->items.used) {
+            if (i >= entry->items.used) {
               if (main.data->error.verbosity != f_console_verbosity_quiet) {
                 f_thread_mutex_lock(&main.thread->lock.print);
 
@@ -649,10 +661,10 @@ extern "C" {
         cache->ats.used -= 2;
         cache->ats.array[at_j]++;
 
-        cache->action.line_item = main.setting->entry.items.array[cache->ats.array[at_i]].line;
+        cache->action.line_item = entry->items.array[cache->ats.array[at_i]].line;
         cache->action.name_item.used = 0;
 
-        status2 = controller_string_dynamic_append_terminated(main.setting->entry.items.array[cache->ats.array[at_i]].name, &cache->action.name_item);
+        status2 = controller_string_dynamic_append_terminated(entry->items.array[cache->ats.array[at_i]].name, &cache->action.name_item);
 
         if (F_status_is_error(status2)) {
           controller_entry_error_print(main.data->error, cache->action, F_status_set_fine(status2), "controller_string_dynamic_append_terminated", F_true, main.thread);
@@ -660,9 +672,9 @@ extern "C" {
           return status2;
         }
       }
-    } // for
+    } // while
 
-    if (!main.thread->enabled) {
+    if (!controller_thread_is_enabled(is_entry, main.thread)) {
       return F_signal;
     }
 
@@ -676,7 +688,7 @@ extern "C" {
 #endif // _di_controller_preprocess_entry_
 
 #ifndef _di_controller_process_entry_
-  f_status_t controller_process_entry(const bool failsafe, const uint8_t action, controller_main_t *main, controller_cache_t *cache) {
+  f_status_t controller_process_entry(const bool failsafe, const bool is_entry, const uint8_t action, controller_main_t *main, controller_cache_t *cache) {
 
     f_status_t status = F_none;
     f_status_t status_lock = F_none;
@@ -690,6 +702,7 @@ extern "C" {
     uint8_t options_force = 0;
     uint8_t options_process = 0;
 
+    controller_entry_t *entry = is_entry ? &main->setting->entry : &main->setting->exit;
     controller_entry_action_t *entry_action = 0;
     controller_entry_actions_t *entry_actions = 0;
     controller_process_t *process = 0;
@@ -708,7 +721,7 @@ extern "C" {
     cache->action.name_item.used = 0;
 
     if (main->setting->ready == controller_setting_ready_yes) {
-      status = controller_perform_ready(*main, cache);
+      status = controller_perform_ready(is_entry, *main, cache);
       if (F_status_is_error(status)) return status;
     }
 
@@ -725,10 +738,10 @@ extern "C" {
     cache->ats.array[1] = 0;
     cache->ats.used = 2;
 
-    cache->action.line_item = main->setting->entry.items.array[cache->ats.array[0]].line;
+    cache->action.line_item = entry->items.array[cache->ats.array[0]].line;
     cache->action.name_item.used = 0;
 
-    status = controller_string_dynamic_append_terminated(main->setting->entry.items.array[cache->ats.array[0]].name, &cache->action.name_item);
+    status = controller_string_dynamic_append_terminated(entry->items.array[cache->ats.array[0]].name, &cache->action.name_item);
 
     if (F_status_is_error(status)) {
       controller_entry_error_print(main->data->error, cache->action, F_status_set_fine(status), "controller_string_dynamic_append_terminated", F_true, main->thread);
@@ -749,11 +762,11 @@ extern "C" {
       }
     }
 
-    for (; main->thread->enabled; ) {
+    while (controller_thread_is_enabled(is_entry, main->thread)) {
 
-      entry_actions = &main->setting->entry.items.array[cache->ats.array[at_i]].actions;
+      entry_actions = &entry->items.array[cache->ats.array[at_i]].actions;
 
-      for (; cache->ats.array[at_j] < entry_actions->used && main->thread->enabled; ++cache->ats.array[at_j]) {
+      for (; cache->ats.array[at_j] < entry_actions->used && controller_thread_is_enabled(is_entry, main->thread); ++cache->ats.array[at_j]) {
 
         entry_action = &entry_actions->array[cache->ats.array[at_j]];
 
@@ -902,7 +915,7 @@ extern "C" {
               }
             }
 
-            controller_rule_wait_all(*main, F_false, process);
+            controller_rule_wait_all(is_entry, *main, F_false, process);
           }
 
           if (main->setting->ready == controller_setting_ready_wait) {
@@ -919,7 +932,7 @@ extern "C" {
               }
             }
             else {
-              controller_perform_ready(*main, cache);
+              controller_perform_ready(is_entry, *main, cache);
 
               if (F_status_is_error(status)) return status;
             }
@@ -941,7 +954,7 @@ extern "C" {
         }
         else if (entry_action->type == controller_entry_action_type_item) {
 
-          if (entry_action->number == 0 || entry_action->number >= main->setting->entry.items.used || failsafe && entry_action->number == main->setting->failsafe_item_id) {
+          if (entry_action->number == 0 || entry_action->number >= entry->items.used || failsafe && entry_action->number == main->setting->failsafe_item_id) {
 
             // 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 (main->data->error.verbosity != f_console_verbosity_quiet) {
@@ -981,9 +994,9 @@ extern "C" {
           cache->action.line_action = 0;
 
           cache->action.name_item.used = 0;
-          cache->action.line_item = main->setting->entry.items.array[cache->ats.array[at_i]].line;
+          cache->action.line_item = entry->items.array[cache->ats.array[at_i]].line;
 
-          status = controller_string_dynamic_append_terminated(main->setting->entry.items.array[cache->ats.array[at_i]].name, &cache->action.name_item);
+          status = controller_string_dynamic_append_terminated(entry->items.array[cache->ats.array[at_i]].name, &cache->action.name_item);
 
           if (F_status_is_error(status)) {
             controller_entry_error_print(main->data->error, cache->action, F_status_set_fine(status), "controller_string_dynamic_append_terminated", F_true, main->thread);
@@ -1009,7 +1022,8 @@ extern "C" {
         }
         else if (entry_action->type == controller_entry_action_type_consider || entry_action->type == controller_entry_action_type_rule) {
 
-          status_lock = controller_lock_write(main->thread, &main->thread->lock.rule);
+          status_lock = controller_lock_write(is_entry, main->thread, &main->thread->lock.rule);
+
           if (status_lock == F_signal || F_status_is_error(status_lock)) {
             controller_lock_error_critical_print(main->data->error, F_status_set_fine(status_lock), F_false, main->thread);
             break;
@@ -1035,7 +1049,8 @@ extern "C" {
           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(main->thread, &main->thread->lock.rule);
+          status_lock = controller_lock_read(is_entry, main->thread, &main->thread->lock.rule);
+
           if (status_lock == F_signal || F_status_is_error(status_lock)) {
             controller_lock_error_critical_print(main->data->error, F_status_set_fine(status_lock), F_true, main->thread);
 
@@ -1059,7 +1074,7 @@ extern "C" {
             }
           }
 
-          if (!main->thread->enabled) break;
+          if (!controller_thread_is_enabled(is_entry, main->thread)) break;
 
           // the rule is not yet loaded, ensure that it is loaded.
           if (status != F_true) {
@@ -1080,14 +1095,15 @@ extern "C" {
             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(main->thread, &main->thread->lock.rule);
+            status_lock = controller_lock_write(is_entry, main->thread, &main->thread->lock.rule);
+
             if (status_lock == F_signal || F_status_is_error(status_lock)) {
               controller_lock_error_critical_print(main->data->error, F_status_set_fine(status_lock), F_false, main->thread);
 
               break;
             }
 
-            status = controller_rule_read(alias_rule, *main, cache, &main->setting->rules.array[main->setting->rules.used]);
+            status = controller_rule_read(is_entry, alias_rule, *main, cache, &main->setting->rules.array[main->setting->rules.used]);
 
             // restore cache.
             memcpy(cache->action.name_action.string, cache_name_action, cache_name_action_used);
@@ -1105,7 +1121,7 @@ extern "C" {
             cache->action.line_action = cache_line_action;
             cache->action.line_item = cache_line_item;
 
-            if (status == F_signal || !main->thread->enabled) {
+            if (status == F_signal || !controller_thread_is_enabled(is_entry, main->thread)) {
               f_thread_unlock(&main->thread->lock.rule);
 
               break;
@@ -1135,7 +1151,8 @@ extern "C" {
 
             // ensure that a process exists for the added rule.
             if (F_status_is_error_not(status)) {
-              status_lock = controller_lock_read(main->thread, &main->thread->lock.process);
+              status_lock = controller_lock_read(is_entry, main->thread, &main->thread->lock.process);
+
               if (status_lock == F_signal || F_status_is_error(status_lock)) {
                 controller_lock_error_critical_print(main->data->error, F_status_set_fine(status_lock), F_true, main->thread);
 
@@ -1145,7 +1162,8 @@ extern "C" {
               if (controller_find_process(alias_rule, main->thread->processs, 0) == F_false) {
                 f_thread_unlock(&main->thread->lock.process);
 
-                status_lock = controller_lock_write(main->thread, &main->thread->lock.process);
+                status_lock = controller_lock_write(is_entry, main->thread, &main->thread->lock.process);
+
                 if (status_lock == F_signal || F_status_is_error(status_lock)) {
                   controller_lock_error_critical_print(main->data->error, F_status_set_fine(status_lock), F_false, main->thread);
 
@@ -1162,7 +1180,8 @@ extern "C" {
                   // only copy the rule alias, as that is all that is needed at this point (the entire rule gets copied prior to executing/processing).
                   controller_process_t *process = main->thread->processs.array[main->thread->processs.used];
 
-                  status_lock = controller_lock_write(main->thread, &process->lock);
+                  status_lock = controller_lock_write(is_entry, main->thread, &process->lock);
+
                   if (status_lock == F_signal || F_status_is_error(status_lock)) {
                     controller_lock_error_critical_print(main->data->error, F_status_set_fine(status_lock), F_false, main->thread);
 
@@ -1223,9 +1242,9 @@ extern "C" {
               }
             }
 
-            status = controller_rule_process_begin(options_force, alias_rule, controller_rule_action_type_start, options_process, stack, *main, *cache);
+            status = controller_rule_process_begin(options_force, alias_rule, is_entry ? controller_rule_action_type_start : controller_rule_action_type_stop, options_process, is_entry ? controller_process_type_entry : controller_process_type_exit, stack, *main, *cache);
 
-            if (F_status_set_fine(status) == F_memory_not || status == F_child || status == F_signal || !main->thread->enabled) {
+            if (F_status_set_fine(status) == F_memory_not || status == F_child || status == F_signal || !controller_thread_is_enabled(is_entry, main->thread)) {
               break;
             }
 
@@ -1290,7 +1309,7 @@ extern "C" {
             }
           }
           else {
-            if (entry_action->number == 0 || entry_action->number >= main->setting->entry.items.used) {
+            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 (main->data->error.verbosity != f_console_verbosity_quiet) {
@@ -1320,7 +1339,7 @@ extern "C" {
                   fprintf(main->data->output.stream, "Processing entry item action '");
                   fprintf(main->data->output.stream, "%s%s%s", main->data->context.set.title.before->string, controller_string_failsafe_s, main->data->context.set.title.after->string);
                   fprintf(main->data->output.stream, "' setting value to '");
-                  fprintf(main->data->output.stream, "%s%s%s", main->data->context.set.important.before->string, main->setting->entry.items.array[main->setting->failsafe_item_id].name.string, main->data->context.set.important.after->string);
+                  fprintf(main->data->output.stream, "%s%s%s", main->data->context.set.important.before->string, entry->items.array[main->setting->failsafe_item_id].name.string, main->data->context.set.important.after->string);
                   fprintf(main->data->output.stream, "'.%c", f_string_eol_s[0]);
 
                   controller_print_unlock_flush(main->data->output.stream, &main->thread->lock.print);
@@ -1331,7 +1350,7 @@ extern "C" {
         }
       } // for
 
-      if (!main->thread->enabled) {
+      if (!controller_thread_is_enabled(is_entry, main->thread)) {
         status = F_signal;
       }
 
@@ -1358,10 +1377,10 @@ extern "C" {
         cache->ats.used -= 2;
         cache->ats.array[at_j]++;
 
-        cache->action.line_item = main->setting->entry.items.array[cache->ats.array[at_i]].line;
+        cache->action.line_item = entry->items.array[cache->ats.array[at_i]].line;
         cache->action.name_item.used = 0;
 
-        status = controller_string_dynamic_append_terminated(main->setting->entry.items.array[cache->ats.array[at_i]].name, &cache->action.name_item);
+        status = controller_string_dynamic_append_terminated(entry->items.array[cache->ats.array[at_i]].name, &cache->action.name_item);
 
         if (F_status_is_error(status)) {
           controller_entry_error_print(main->data->error, cache->action, F_status_set_fine(status), "controller_string_dynamic_append_terminated", F_true, main->thread);
@@ -1369,23 +1388,23 @@ extern "C" {
           break;
         }
       }
-    } // for
+    } // while
 
-    if (!main->thread->enabled) {
+    if (!controller_thread_is_enabled(is_entry, main->thread)) {
       return F_signal;
     }
 
-    if (status == F_child || status == F_signal) {
+    if (status == F_child) {
       return status;
     }
 
-    if (status_lock == F_signal || F_status_is_error(status_lock)) {
+    if (F_status_is_error(status_lock)) {
       return status_lock;
     }
 
     // check to see if any requied processes failed, but do not do this if already operating in failsafe.
     if (F_status_is_error_not(status) && !failsafe) {
-      const f_status_t status_wait = controller_rule_wait_all(*main, F_true, 0);
+      const f_status_t status_wait = controller_rule_wait_all(is_entry, *main, F_true, 0);
 
       if (status_wait == F_signal || F_status_is_error(status_wait)) {
         return status_wait;
index cef422a97b071eab67984ec775f5dffc521c2ad7..66e59af2aab7c16bbf882e683deb87b153742a55 100644 (file)
@@ -44,10 +44,10 @@ extern "C" {
  * @return
  *   F_none on success.
  *
- *   Errors (with error bit) from: f_string_dynamic_append().
+ *   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()
+ * @see f_string_dynamic_append_nulless()
  * @see f_string_dynamic_terminate_after()
  */
 #ifndef _di_controller_string_dynamic_append_terminated_
@@ -80,6 +80,9 @@ extern "C" {
 /**
  * Load a file from the controller settings directory.
  *
+ * @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
@@ -100,6 +103,7 @@ extern "C" {
  *
  * @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().
@@ -114,7 +118,7 @@ extern "C" {
  * @see f_string_dynamic_terminate_after()
  */
 #ifndef _di_controller_file_load_
-  extern f_status_t controller_file_load(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_main_t main, controller_cache_t *cache) f_gcc_attribute_visibility_internal;
+  extern f_status_t controller_file_load(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_main_t main, controller_cache_t *cache) f_gcc_attribute_visibility_internal;
 #endif // _di_controller_file_load_
 
 /**
@@ -272,6 +276,9 @@ extern "C" {
  *
  * This does not do any locking or unlocking for the setting data, be sure to lock appropriately before and after calling this.
  *
+ * @param is_entry
+ *   If TRUE, then this operate as an entry.
+ *   If FALSE, then this operate as an exit.
  * @param main
  *   The main data.
  * @param cache
@@ -285,12 +292,15 @@ extern "C" {
  * @see controller_file_pid_create()
  */
 #ifndef _di_controller_perform_ready_
-  extern f_status_t controller_perform_ready(controller_main_t main, controller_cache_t *cache) f_gcc_attribute_visibility_internal;
+  extern f_status_t controller_perform_ready(const bool is_entry, controller_main_t main, controller_cache_t *cache) f_gcc_attribute_visibility_internal;
 #endif // _di_controller_perform_ready_
 
 /**
  * Pre-process all items for the loaded entry.
  *
+ * @param is_entry
+ *   If TRUE, then this operate as an entry.
+ *   If FALSE, then this operate as an exit.
  * @param main
  *   The main data.
  * @param cache
@@ -313,7 +323,7 @@ extern "C" {
  * @see f_string_dynamic_terminate_after()
  */
 #ifndef _di_controller_preprocess_entry_
-  extern f_status_t controller_preprocess_entry(controller_main_t main, controller_cache_t *cache) f_gcc_attribute_visibility_internal;
+  extern f_status_t controller_preprocess_entry(const bool is_entry, controller_main_t main, controller_cache_t *cache) f_gcc_attribute_visibility_internal;
 #endif // _di_controller_preprocess_entry_
 
 /**
@@ -322,6 +332,9 @@ extern "C" {
  * @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.
  * @param action
  *   The action to perform, should be either controller_rule_action_type_start (for entry) or controller_rule_action_type_stop (for exit).
  * @param main
@@ -344,7 +357,7 @@ extern "C" {
  * @see controller_string_dynamic_append_terminated()
  */
 #ifndef _di_controller_process_entry_
-  extern f_status_t controller_process_entry(const bool failsafe, const uint8_t action, controller_main_t *main, controller_cache_t *cache) f_gcc_attribute_visibility_internal;
+  extern f_status_t controller_process_entry(const bool failsafe, const bool is_entry, const uint8_t action, controller_main_t *main, controller_cache_t *cache) f_gcc_attribute_visibility_internal;
 #endif // _di_controller_process_entry_
 
 /**
index 7b9eae72d26e1355a76dc7d9c9cc1385f79ddf77..066b9ab6e5e1564afaeaebbdd28b3d6fc086b72e 100644 (file)
@@ -625,11 +625,14 @@ extern "C" {
 #endif // _di_controller_entry_items_increase_by_
 
 #ifndef _di_controller_entry_read_
-  f_status_t controller_entry_read(const f_string_static_t entry_name, controller_main_t main, controller_cache_t *cache) {
+  f_status_t controller_entry_read(const bool is_entry, controller_main_t main, controller_cache_t *cache) {
+
     f_status_t status = F_none;
 
-    main.setting->entry.status = F_known_not;
-    main.setting->entry.items.used = 0;
+    controller_entry_t *entry = is_entry ? &main.setting->entry : &main.setting->exit;
+
+    entry->status = F_known_not;
+    entry->items.used = 0;
 
     cache->action.line_action = 0;
     cache->action.line_item = 0;
@@ -666,7 +669,16 @@ extern "C" {
     cache->action.name_action.used = 0;
     cache->action.name_item.used = 0;
 
-    status = controller_file_load(controller_string_entries_s, entry_name, controller_string_entry_s, controller_string_entries_length, controller_string_entry_length, main, cache);
+    if (is_entry) {
+      status = controller_file_load(F_true, controller_string_entries_s, main.setting->name_entry, controller_string_entry_s, controller_string_entries_length, controller_string_entry_length, main, cache);
+    }
+    else {
+      status = controller_file_load(F_false, controller_string_exits_s, main.setting->name_entry, controller_string_exit_s, controller_string_exits_length, controller_string_exit_length, main, cache);
+
+      if (status == F_file_found_not) {
+        return F_file_found_not;
+      }
+    }
 
     if (F_status_is_error_not(status)) {
       if (cache->buffer_file.used) {
@@ -690,7 +702,7 @@ extern "C" {
           f_thread_mutex_lock(&main.thread->lock.print);
 
           fprintf(main.data->error.to.stream, "%c", f_string_eol_s[0]);
-          fprintf(main.data->error.to.stream, "%s%sThe entry file is empty.%s%c", main.data->error.context.before->string, main.data->error.prefix ? main.data->error.prefix : f_string_empty_s, main.data->error.context.after->string, f_string_eol_s[0]);
+          fprintf(main.data->error.to.stream, "%s%sThe %s file is empty.%s%c", main.data->error.context.before->string, main.data->error.prefix ? main.data->error.prefix : f_string_empty_s, is_entry ? controller_string_entry_s : controller_string_exit_s, main.data->error.context.after->string, f_string_eol_s[0]);
 
           controller_print_unlock_flush(main.data->error.to.stream, &main.thread->lock.print);
         }
@@ -700,7 +712,7 @@ extern "C" {
     }
 
     if (F_status_is_error_not(status) && cache->object_items.used) {
-      status = controller_entry_items_increase_by(cache->object_items.used, &main.setting->entry.items);
+      status = controller_entry_items_increase_by(cache->object_items.used, &entry->items);
 
       if (F_status_is_error(status)) {
         controller_entry_error_print(main.data->error, cache->action, F_status_set_fine(status), "controller_entry_items_increase_by", F_true, main.thread);
@@ -716,11 +728,7 @@ extern "C" {
         f_array_length_t i = 0;
         f_array_length_t j = 0;
 
-        for (; i < cache->object_items.used && main.thread->enabled; ++i) {
-
-          if (!main.thread->enabled) {
-            return F_signal;
-          }
+        for (; i < cache->object_items.used && controller_thread_is_enabled(is_entry, main.thread); ++i) {
 
           if (code & 0x2) {
             code -= 0x2;
@@ -745,7 +753,7 @@ extern "C" {
           cache->action.name_action.used = 0;
           cache->action.name_item.used = 0;
 
-          status = controller_entry_items_increase_by(controller_default_allocation_step, &main.setting->entry.items);
+          status = controller_entry_items_increase_by(controller_default_allocation_step, &entry->items);
 
           if (F_status_is_error(status)) {
             controller_entry_error_print(main.data->error, cache->action, F_status_set_fine(status), "controller_entry_items_increase_by", F_true, main.thread);
@@ -768,14 +776,14 @@ extern "C" {
 
           cache->action.line_item++;
 
-          for (j = (code & 0x1) ? 1 : 0; j < main.setting->entry.items.used; ++j) {
+          for (j = (code & 0x1) ? 1 : 0; j < entry->items.used; ++j) {
 
-            if (fl_string_dynamic_compare(main.setting->entry.items.array[j].name, cache->action.name_item) == F_equal_to) {
+            if (fl_string_dynamic_compare(entry->items.array[j].name, cache->action.name_item) == F_equal_to) {
               if (main.data->warning.verbosity == f_console_verbosity_debug) {
                 f_thread_mutex_lock(&main.thread->lock.print);
 
                 fprintf(main.data->warning.to.stream, "%c", f_string_eol_s[0]);
-                fprintf(main.data->warning.to.stream, "%s%sIgnoring duplicate entry item '", main.data->warning.context.before->string, main.data->warning.prefix ? main.data->warning.prefix : f_string_empty_s);
+                fprintf(main.data->warning.to.stream, "%s%sIgnoring duplicate %s item '", main.data->warning.context.before->string, main.data->warning.prefix ? main.data->warning.prefix : f_string_empty_s, is_entry ? controller_string_entry_s : controller_string_exit_s);
                 fprintf(main.data->warning.to.stream, "%s%s%s%s", main.data->warning.context.after->string, main.data->warning.notable.before->string, cache->action.name_file.string, main.data->warning.notable.after->string);
                 fprintf(main.data->warning.to.stream, "%s'.%s%c", main.data->warning.context.before->string, main.data->warning.context.after->string, f_string_eol_s[0]);
 
@@ -798,30 +806,30 @@ extern "C" {
 
             at = 0;
 
-            if (!main.setting->entry.items.used) {
-              main.setting->entry.items.used = 1;
+            if (!entry->items.used) {
+              entry->items.used = 1;
             }
           }
           else if (fl_string_dynamic_compare_string(controller_string_setting_s, cache->action.name_item, controller_string_setting_length) == F_equal_to) {
-            status = controller_entry_settings_read(*range, main, cache);
+            status = controller_entry_settings_read(is_entry, *range, main, cache);
 
             continue;
           }
-          else if (main.setting->entry.items.used) {
-            at = main.setting->entry.items.used++;
+          else if (entry->items.used) {
+            at = entry->items.used++;
           }
           else {
 
             // skip position 0, which is reserved for "main".
-            main.setting->entry.items.array[0].name.used = 0;
+            entry->items.array[0].name.used = 0;
 
             at = 1;
-            main.setting->entry.items.used = 2;
+            entry->items.used = 2;
           }
 
-          main.setting->entry.items.array[at].line = cache->action.line_item;
+          entry->items.array[at].line = cache->action.line_item;
 
-          status = controller_string_dynamic_append_terminated(cache->action.name_item, &main.setting->entry.items.array[at].name);
+          status = controller_string_dynamic_append_terminated(cache->action.name_item, &entry->items.array[at].name);
 
           if (F_status_is_error(status)) {
             controller_error_print(main.data->error, F_status_set_fine(status), "controller_string_dynamic_append_terminated", F_true, main.thread);
@@ -829,7 +837,7 @@ extern "C" {
             break;
           }
 
-          status = controller_entry_actions_read(*range, main, cache, &main.setting->entry.items.array[at].actions);
+          status = controller_entry_actions_read(*range, main, cache, &entry->items.array[at].actions);
 
           if (F_status_is_error(status)) {
             f_thread_mutex_lock(&main.thread->lock.print);
@@ -844,6 +852,10 @@ extern "C" {
           }
         } // for
 
+        if (is_entry && status == F_signal) {
+          return F_signal;
+        }
+
         if (F_status_is_error_not(status)) {
           cache->action.name_action.used = 0;
           cache->action.name_item.used = 0;
@@ -853,7 +865,7 @@ extern "C" {
               f_thread_mutex_lock(&main.thread->lock.print);
 
               fprintf(main.data->error.to.stream, "%c", f_string_eol_s[0]);
-              fprintf(main.data->error.to.stream, "%s%sThe required entry item '", main.data->error.context.before->string, main.data->error.prefix ? main.data->error.prefix : f_string_empty_s);
+              fprintf(main.data->error.to.stream, "%s%sThe required %s item '", main.data->error.context.before->string, main.data->error.prefix ? main.data->error.prefix : f_string_empty_s, is_entry ? controller_string_entry_s : controller_string_exit_s);
               fprintf(main.data->error.to.stream, "%s%s%s%s", main.data->error.context.after->string, main.data->error.notable.before->string, controller_string_main_s, main.data->error.notable.after->string);
               fprintf(main.data->error.to.stream, "%s' was not found.%s%c", main.data->error.context.before->string, main.data->error.context.after->string, f_string_eol_s[0]);
 
@@ -871,15 +883,15 @@ extern "C" {
             // 0x1 = missing or not, 0x2 = one or more missing.
             uint8_t missing = 0;
 
-            for (i = 0; i < main.setting->entry.items.used; ++i) {
+            for (i = 0; i < entry->items.used; ++i) {
 
-              for (j = 0; j < main.setting->entry.items.array[i].actions.used; ++j) {
+              for (j = 0; j < entry->items.array[i].actions.used; ++j) {
 
-                if (!main.thread->enabled) {
+                if (!controller_thread_is_enabled(is_entry, main.thread)) {
                   return F_signal;
                 }
 
-                action = &main.setting->entry.items.array[i].actions.array[j];
+                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;
@@ -887,9 +899,9 @@ extern "C" {
                 if (action->type == controller_entry_action_type_failsafe || action->type == controller_entry_action_type_item) {
                   missing |= 0x1;
 
-                  for (k = 0; k < main.setting->entry.items.used; ++k) {
+                  for (k = 0; k < entry->items.used; ++k) {
 
-                    if (fl_string_dynamic_compare(action->parameters.array[0], main.setting->entry.items.array[k].name) == F_equal_to) {
+                    if (fl_string_dynamic_compare(action->parameters.array[0], entry->items.array[k].name) == F_equal_to) {
                       if (missing & 0x1) {
                         missing -= 0x1;
                       }
@@ -902,9 +914,9 @@ extern "C" {
                     missing |= 0x2;
 
                     cache->action.line_action = action->line;
-                    cache->action.line_item = main.setting->entry.items.array[i].line;
+                    cache->action.line_item = entry->items.array[i].line;
 
-                    status = controller_string_dynamic_append_terminated(main.setting->entry.items.array[i].name, &cache->action.name_item);
+                    status = controller_string_dynamic_append_terminated(entry->items.array[i].name, &cache->action.name_item);
 
                     if (F_status_is_error(status)) {
                       controller_error_print(main.data->error, F_status_set_fine(status), "controller_string_dynamic_append_terminated", F_true, main.thread);
@@ -915,7 +927,7 @@ extern "C" {
                       f_thread_mutex_lock(&main.thread->lock.print);
 
                       fprintf(main.data->error.to.stream, "%c", f_string_eol_s[0]);
-                      fprintf(main.data->error.to.stream, "%s%sThe required entry item '", main.data->error.context.before->string, main.data->error.prefix ? main.data->error.prefix : f_string_empty_s);
+                      fprintf(main.data->error.to.stream, "%s%sThe required %s item '", main.data->error.context.before->string, main.data->error.prefix ? main.data->error.prefix : f_string_empty_s, is_entry ? controller_string_entry_s : controller_string_exit_s);
                       fprintf(main.data->error.to.stream, "%s%s%s%s", main.data->error.context.after->string, main.data->error.notable.before->string, action->parameters.array[0].string, main.data->error.notable.after->string);
                       fprintf(main.data->error.to.stream, "%s' does not exist.%s%c", main.data->error.context.before->string, main.data->error.context.after->string, f_string_eol_s[0]);
 
@@ -944,18 +956,18 @@ extern "C" {
     if (F_status_is_error(status)) {
       controller_entry_error_print_cache(main.data->error, cache->action);
 
-      main.setting->entry.status = controller_status_simplify_error(F_status_set_fine(status));
+      entry->status = controller_status_simplify_error(F_status_set_fine(status));
     }
     else {
-      main.setting->entry.status = F_none;
+      entry->status = F_none;
     }
 
-    return main.setting->entry.status;
+    return entry->status;
   }
 #endif // _di_controller_entry_read_
 
 #ifndef _di_controller_entry_settings_read_
-  f_status_t controller_entry_settings_read(const f_string_range_t content_range, controller_main_t main, controller_cache_t *cache) {
+  f_status_t controller_entry_settings_read(const bool is_entry, const f_string_range_t content_range, controller_main_t main, controller_cache_t *cache) {
     f_status_t status = F_none;
 
     {
@@ -1002,11 +1014,11 @@ extern "C" {
         break;
       }
 
-      if (fl_string_dynamic_compare_string(controller_string_mode_s, cache->action.name_action, controller_string_mode_length) == F_equal_to) {
+      if (is_entry && fl_string_dynamic_compare_string(controller_string_mode_s, cache->action.name_action, controller_string_mode_length) == F_equal_to) {
         if (cache->content_actions.array[i].used < 0 || cache->content_actions.array[i].used > 1) {
           if (main.data->error.verbosity != f_console_verbosity_quiet) {
             fprintf(main.data->error.to.stream, "%c", f_string_eol_s[0]);
-            fprintf(main.data->error.to.stream, "%s%sThe entry item setting '", main.data->error.context.before->string, main.data->error.prefix ? main.data->error.prefix : f_string_empty_s);
+            fprintf(main.data->error.to.stream, "%s%sThe %s item setting '", main.data->error.context.before->string, main.data->error.prefix ? main.data->error.prefix : f_string_empty_s, is_entry ? controller_string_entry_s : controller_string_exit_s);
             fprintf(main.data->error.to.stream, "%s%s%s%s", main.data->error.context.after->string, main.data->error.notable.before->string, cache->action.name_action.string, main.data->error.notable.after->string);
             fprintf(main.data->error.to.stream, "%s' requires exactly ", main.data->error.context.before->string);
             fprintf(main.data->error.to.stream, "%s%s%llu%s", main.data->error.context.after->string, main.data->error.notable.before->string, 1, main.data->error.notable.after->string);
@@ -1024,10 +1036,10 @@ extern "C" {
         }
         else {
           if (main.data->warning.verbosity == f_console_verbosity_debug) {
-            fprintf(main.data->warning.to.stream, "%s%sUnknown entry item setting value '", main.data->warning.context.before->string, main.data->warning.prefix ? main.data->warning.prefix : f_string_empty_s);
+            fprintf(main.data->warning.to.stream, "%s%sUnknown %s item setting value '", main.data->warning.context.before->string, main.data->warning.prefix ? main.data->warning.prefix : f_string_empty_s, is_entry ? controller_string_entry_s : controller_string_exit_s);
             fprintf(main.data->warning.to.stream, "%s%s", main.data->warning.context.after->string, main.data->warning.notable.before->string);
             f_print_dynamic_partial(main.data->warning.to.stream, cache->buffer_file, cache->content_actions.array[i].array[0]);
-            fprintf(main.data->warning.to.stream, "%s%s for entry item setting '", main.data->warning.notable.after->string, main.data->warning.context.before->string);
+            fprintf(main.data->warning.to.stream, "%s%s for %s item setting '", main.data->warning.notable.after->string, main.data->warning.context.before->string, is_entry ? controller_string_entry_s : controller_string_exit_s);
             fprintf(main.data->warning.to.stream, "%s%s", main.data->warning.context.after->string, main.data->warning.notable.before->string);
             f_print_dynamic(main.data->warning.to.stream, cache->action.name_action);
             fprintf(main.data->warning.to.stream, "%s", main.data->warning.notable.after->string);
@@ -1041,7 +1053,7 @@ extern "C" {
       }
       else {
         if (main.data->warning.verbosity == f_console_verbosity_debug) {
-          fprintf(main.data->warning.to.stream, "%s%sUnknown entry item setting '", main.data->warning.context.before->string, main.data->warning.prefix ? main.data->warning.prefix : f_string_empty_s);
+          fprintf(main.data->warning.to.stream, "%s%sUnknown %s item setting '", main.data->warning.context.before->string, main.data->warning.prefix ? main.data->warning.prefix : f_string_empty_s, is_entry ? controller_string_entry_s : controller_string_exit_s);
           fprintf(main.data->warning.to.stream, "%s%s", main.data->warning.context.after->string, main.data->warning.notable.before->string);
           f_print_dynamic(main.data->warning.to.stream, cache->action.name_action);
           fprintf(main.data->warning.to.stream, "%s", main.data->warning.notable.after->string);
index cfe585e4beeaab09b93761d443c88e0fd59446f6..e791b6cee9bc0e7ccf87418cd11b6cda174cdb7b 100644 (file)
@@ -166,10 +166,9 @@ extern "C" {
 /**
  * Read the entry, extracting all lists.
  *
- * @param entry_name
- *   The string identifying the entry.
- *   This is constructed from the path parts to the file without the file extension and without the settings directory prefix.
- *   "/etc/controller/entries/example/my.entry" would have a rule id of "example/my".
+ * @param is_entry
+ *   If TRUE, then this loads as an entry.
+ *   If FALSE, then this loads as an exit.
  * @param main
  *   The main data.
  * @param cache
@@ -178,6 +177,7 @@ extern "C" {
  *
  * @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().
@@ -206,12 +206,15 @@ extern "C" {
  * @see fll_fss_basic_list_read()
  */
 #ifndef _di_controller_entry_read_
-  extern f_status_t controller_entry_read(const f_string_static_t entry_name, controller_main_t main, controller_cache_t *cache) f_gcc_attribute_visibility_internal;
+  extern f_status_t controller_entry_read(const bool is_entry, controller_main_t main, controller_cache_t *cache) f_gcc_attribute_visibility_internal;
 #endif // _di_controller_entry_read_
 
 /**
  * Read the entry settings, loading all settings.
  *
+ * @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 main
@@ -220,7 +223,7 @@ extern "C" {
  *   A structure for containing and caching relevant data.
  */
 #ifndef _di_controller_entry_settings_read_
-  extern f_status_t controller_entry_settings_read(const f_string_range_t content_range, controller_main_t main, controller_cache_t *cache) f_gcc_attribute_visibility_internal;
+  extern f_status_t controller_entry_settings_read(const bool is_entry, const f_string_range_t content_range, controller_main_t main, controller_cache_t *cache) f_gcc_attribute_visibility_internal;
 #endif // _di_controller_entry_settings_read_
 
 #ifdef __cplusplus
index fcf4a997c87d035a250f7f8e4fe47213619bf19a..97e75cd6923e473fb067abd95d9c1002adac67f3 100644 (file)
@@ -42,6 +42,7 @@ extern "C" {
 
       if (fl_string_dynamic_compare(alias, rules.array[i].alias) == F_equal_to) {
         if (at) *at = i;
+
         return F_true;
       }
     } // for
@@ -604,7 +605,6 @@ extern "C" {
   }
 #endif // _di_controller_rule_copy_
 
-
 #ifndef _di_controller_rule_error_print_
   void controller_rule_error_print(const fll_error_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, controller_thread_t *thread) {
 
@@ -835,7 +835,7 @@ extern "C" {
       return status;
     }
 
-    for (i = 0; i < process->rule.items.used && main.thread->enabled; ++i) {
+    for (i = 0; i < process->rule.items.used && controller_thread_is_enabled_process(process, main.thread); ++i) {
 
       if (process->rule.items.array[i].type == controller_rule_item_type_setting) continue;
 
@@ -855,8 +855,9 @@ extern "C" {
 
       for (j = 0; j < process->rule.items.array[i].actions.used; ++j) {
 
-        if (!main.thread->enabled) {
+        if (!controller_thread_is_enabled_process(process, main.thread)) {
           status = F_signal;
+
           break;
         }
 
@@ -1012,7 +1013,8 @@ extern "C" {
 
     // lock failed, attempt to re-establish lock before returning.
     if (F_status_set_fine(status) == F_lock) {
-      status = controller_lock_read(main.thread, &process->lock);
+      status = controller_lock_read(process, main.thread, &process->lock);
+
       if (status == F_signal || F_status_is_error(status)) {
         return F_status_set_error(F_lock);
       }
@@ -1020,11 +1022,11 @@ extern "C" {
       success = F_false;
     }
 
-    if (!main.thread->enabled) {
+    if (!controller_thread_is_enabled_process(process, main.thread)) {
       return F_signal;
     }
 
-    if (status == F_child || status == F_signal || F_status_is_error(status)) {
+    if (status == F_child || F_status_is_error(status)) {
       return status;
     }
 
@@ -1087,12 +1089,13 @@ extern "C" {
 
       f_thread_unlock(&process->lock);
 
-      status_lock = controller_lock_write(main.thread, &process->lock);
+      status_lock = controller_lock_write_process(process, main.thread, &process->lock);
+
       if (status_lock == F_signal || F_status_is_error(status_lock)) {
         controller_lock_error_critical_print(main.data->error, F_status_set_fine(status_lock), F_false, main.thread);
 
         if (status_lock != F_signal) {
-          status = controller_lock_read(main.thread, &process->lock);
+          status = controller_lock_read_process(process, main.thread, &process->lock);
 
           if (status == F_none) {
             return status_lock;
@@ -1107,15 +1110,19 @@ extern "C" {
 
       f_thread_unlock(&process->lock);
 
-      status_lock = controller_lock_read(main.thread, &process->lock);
+      status_lock = controller_lock_read_process(process, main.thread, &process->lock);
+
       if (status_lock == F_signal || F_status_is_error(status_lock)) {
         controller_lock_error_critical_print(main.data->error, F_status_set_fine(status_lock), F_true, main.thread);
       }
 
-      // have the parent wait for the child process to finish.
-      waitpid(id_child, &result, 0);
+      if (status_lock != F_signal) {
+
+        // have the parent wait for the child process to finish.
+        waitpid(id_child, &result, 0);
+      }
 
-      if (!main.thread->enabled) {
+      if (status_lock == F_signal || !controller_thread_is_enabled_process(process, main.thread)) {
         if (status_lock == F_none) {
           return F_signal;
         }
@@ -1127,12 +1134,13 @@ extern "C" {
         f_thread_unlock(&process->lock);
       }
 
-      status_lock = controller_lock_write(main.thread, &process->lock);
+      status_lock = controller_lock_write_process(process, main.thread, &process->lock);
+
       if (status_lock == F_signal || F_status_is_error(status_lock)) {
         controller_lock_error_critical_print(main.data->error, F_status_set_fine(status_lock), F_false, main.thread);
 
         if (status_lock != F_signal) {
-          status = controller_lock_read(main.thread, &process->lock);
+          status = controller_lock_read_process(process, main.thread, &process->lock);
 
           if (status == F_none) {
             return status_lock;
@@ -1147,7 +1155,8 @@ extern "C" {
 
       f_thread_unlock(&process->lock);
 
-      status_lock = controller_lock_read(main.thread, &process->lock);
+      status_lock = controller_lock_read_process(process, main.thread, &process->lock);
+
       if (status_lock == F_signal || F_status_is_error(status_lock)) {
         controller_lock_error_critical_print(main.data->error, F_status_set_fine(status_lock), F_true, main.thread);
 
@@ -1163,7 +1172,7 @@ extern "C" {
       }
     }
     else {
-      if (!main.thread->enabled) {
+      if (!controller_thread_is_enabled_process(process, main.thread)) {
         return F_signal;
       }
     }
@@ -1283,12 +1292,13 @@ extern "C" {
 
       f_thread_unlock(&process->lock);
 
-      status_lock = controller_lock_write(main.thread, &process->lock);
+      status_lock = controller_lock_write_process(process, main.thread, &process->lock);
+
       if (status_lock == F_signal || F_status_is_error(status_lock)) {
         controller_lock_error_critical_print(main.data->error, F_status_set_fine(status_lock), F_false, main.thread);
 
         if (status_lock != F_signal) {
-          status = controller_lock_read(main.thread, &process->lock);
+          status = controller_lock_read_process(process, main.thread, &process->lock);
 
           if (status == F_none) {
             return status_lock;
@@ -1303,15 +1313,19 @@ extern "C" {
 
       f_thread_unlock(&process->lock);
 
-      status_lock = controller_lock_read(main.thread, &process->lock);
+      status_lock = controller_lock_read_process(process, main.thread, &process->lock);
+
       if (status_lock == F_signal || F_status_is_error(status_lock)) {
         controller_lock_error_critical_print(main.data->error, F_status_set_fine(status_lock), F_true, main.thread);
       }
 
-      // 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, 0);
+      if (status_lock != F_signal) {
+
+        // 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, 0);
+      }
 
-      if (!main.thread->enabled) {
+      if (!controller_thread_is_enabled_process(process, main.thread)) {
         if (status_lock == F_none) {
           return F_signal;
         }
@@ -1323,12 +1337,13 @@ extern "C" {
         f_thread_unlock(&process->lock);
       }
 
-      status_lock = controller_lock_write(main.thread, &process->lock);
+      status_lock = controller_lock_write_process(process, main.thread, &process->lock);
+
       if (status_lock == F_signal || F_status_is_error(status_lock)) {
         controller_lock_error_critical_print(main.data->error, F_status_set_fine(status_lock), F_false, main.thread);
 
         if (status_lock != F_signal) {
-          status = controller_lock_read(main.thread, &process->lock);
+          status = controller_lock_read_process(process, main.thread, &process->lock);
 
           if (status == F_none) {
             return status_lock;
@@ -1343,7 +1358,8 @@ extern "C" {
 
       f_thread_unlock(&process->lock);
 
-      status_lock = controller_lock_read(main.thread, &process->lock);
+      status_lock = controller_lock_read_process(process, main.thread, &process->lock);
+
       if (status_lock == F_signal || F_status_is_error(status_lock)) {
         controller_lock_error_critical_print(main.data->error, F_status_set_fine(status_lock), F_true, main.thread);
 
@@ -1360,7 +1376,7 @@ extern "C" {
       }
     }
     else {
-      if (!main.thread->enabled) {
+      if (!controller_thread_is_enabled_process(process, main.thread)) {
         return F_signal;
       }
     }
@@ -1852,11 +1868,11 @@ extern "C" {
       f_array_length_t j = 0;
       f_array_length_t k = 0;
       f_array_length_t id_rule = 0;
-      f_array_length_t id_process = 0;
+      f_array_length_t id_dependency = 0;
 
       bool found = F_false;
 
-      controller_process_t *process_other = 0;
+      controller_process_t *dependency = 0;
 
       uint8_t options_process = 0;
 
@@ -1874,39 +1890,43 @@ extern "C" {
 
       // 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 && main.thread->enabled; ++i) {
+      for (i = 0; i < 3 && controller_thread_is_enabled_process(process, main.thread); ++i) {
 
-        for (j = 0; j < dynamics[i]->used && main.thread->enabled; ++j) {
+        for (j = 0; j < dynamics[i]->used && controller_thread_is_enabled_process(process, main.thread); ++j) {
 
-          process_other = 0;
+          id_dependency = 0;
+          dependency = 0;
           found = F_false;
 
-          status_lock = controller_lock_read(main.thread, &main.thread->lock.process);
+          status_lock = controller_lock_read_process(process, main.thread, &main.thread->lock.process);
+
           if (status_lock == F_signal || F_status_is_error(status_lock)) {
             controller_lock_error_critical_print(main.data->error, F_status_set_fine(status_lock), F_true, main.thread);
           }
           else {
-            status = controller_find_process(dynamics[i]->array[j], main.thread->processs, &id_process);
+            status = controller_find_process(dynamics[i]->array[j], main.thread->processs, &id_dependency);
           }
 
           if (status == F_true) {
             found = F_true;
 
-            process_other = main.thread->processs.array[id_process];
+            dependency = main.thread->processs.array[id_dependency];
+
+            status_lock = controller_lock_read_process(process, main.thread, &dependency->active);
 
-            status_lock = controller_lock_read(main.thread, &process_other->active);
             if (status_lock == F_signal || F_status_is_error(status_lock)) {
               controller_lock_error_critical_print(main.data->error, F_status_set_fine(status_lock), F_true, main.thread);
 
               status = F_false;
-              process_other = 0;
+              dependency = 0;
 
               f_thread_unlock(&main.thread->lock.process);
             }
             else {
               f_thread_unlock(&main.thread->lock.process);
 
-              status_lock = controller_lock_read(main.thread, &main.thread->lock.rule);
+              status_lock = controller_lock_read_process(process, main.thread, &main.thread->lock.rule);
+
               if (status_lock == F_signal || F_status_is_error(status_lock)) {
                 controller_lock_error_critical_print(main.data->error, F_status_set_fine(status_lock), F_true, main.thread);
 
@@ -1938,8 +1958,8 @@ extern "C" {
               status = F_status_set_error(F_found_not);
 
               if (!(process->options & controller_process_option_simulate)) {
-                if (process_other) {
-                  f_thread_unlock(&process_other->active);
+                if (dependency) {
+                  f_thread_unlock(&dependency->active);
                 }
 
                 break;
@@ -1957,7 +1977,8 @@ extern "C" {
             }
           }
           else if (found) {
-            status_lock = controller_lock_read(main.thread, &main.thread->lock.rule);
+            status_lock = controller_lock_read_process(process, main.thread, &main.thread->lock.rule);
+
             if (status_lock == F_signal || F_status_is_error(status_lock)) {
               controller_lock_error_critical_print(main.data->error, F_status_set_fine(status_lock), F_true, main.thread);
 
@@ -1968,7 +1989,7 @@ extern "C" {
 
           if (found) {
 
-            // the process_other may have write locks, which needs to be avoided, so copy the alias from the rule.
+            // the dependency may have write locks, which needs to be avoided, so copy the alias from the rule.
             char alias_other_buffer[main.setting->rules.array[id_rule].alias.used + 1];
 
             memcpy(alias_other_buffer, main.setting->rules.array[id_rule].alias.string, sizeof(char) * main.setting->rules.array[id_rule].alias.used);
@@ -1978,33 +1999,35 @@ extern "C" {
 
             f_thread_unlock(&main.thread->lock.rule);
 
-            status_lock = controller_lock_read(main.thread, &process_other->lock);
+            status_lock = controller_lock_read_process(process, main.thread, &dependency->lock);
+
             if (status_lock == F_signal || F_status_is_error(status_lock)) {
               controller_lock_error_critical_print(main.data->error, F_status_set_fine(status_lock), F_true, main.thread);
 
               status = status_lock;
             }
-            else if (process_other->state == controller_process_state_active || process_other->state == controller_process_state_busy) {
-              f_thread_unlock(&process_other->lock);
+            else if (dependency->state == controller_process_state_active || dependency->state == controller_process_state_busy) {
+              f_thread_unlock(&dependency->lock);
 
-              status = controller_process_wait(main, process_other);
+              status = controller_process_wait(main, dependency);
 
               if (F_status_is_error(status) && !(process->options & controller_process_option_simulate)) break;
 
-              status = process_other->rule.status;
+              status = dependency->rule.status;
             }
             else {
-              status_lock = controller_lock_read(main.thread, &main.thread->lock.rule);
+              status_lock = controller_lock_read_process(process, main.thread, &main.thread->lock.rule);
+
               if (status_lock == F_signal || F_status_is_error(status_lock)) {
                 controller_lock_error_critical_print(main.data->error, F_status_set_fine(status_lock), F_true, main.thread);
 
-                f_thread_unlock(&process_other->lock);
+                f_thread_unlock(&dependency->lock);
 
                 status = status_lock;
               }
               else if (main.setting->rules.array[id_rule].status == F_known_not) {
                 f_thread_unlock(&main.thread->lock.rule);
-                f_thread_unlock(&process_other->lock);
+                f_thread_unlock(&dependency->lock);
 
                 options_process = 0;
 
@@ -2017,10 +2040,10 @@ extern "C" {
                 }
 
                 // synchronously execute dependency.
-                status = controller_rule_process_begin(0, alias_other, process->action, options_process, process->stack, main, process_other->cache);
+                status = controller_rule_process_begin(0, alias_other, process->action, options_process, process->type, process->stack, main, dependency->cache);
 
                 if (status == F_child || status == F_signal) {
-                  f_thread_unlock(&process_other->active);
+                  f_thread_unlock(&dependency->active);
 
                   break;
                 }
@@ -2034,8 +2057,8 @@ extern "C" {
 
                     controller_print_unlock_flush(main.data->error.to.stream, &main.thread->lock.print);
 
-                    if (!(process_other->options & controller_process_option_simulate) || F_status_set_fine(status) == F_memory_not) {
-                      f_thread_unlock(&process_other->active);
+                    if (!(dependency->options & controller_process_option_simulate) || F_status_set_fine(status) == F_memory_not) {
+                      f_thread_unlock(&dependency->active);
 
                       break;
                     }
@@ -2056,18 +2079,19 @@ extern "C" {
                 status = main.setting->rules.array[id_rule].status;
 
                 f_thread_unlock(&main.thread->lock.rule);
-                f_thread_unlock(&process_other->lock);
+                f_thread_unlock(&dependency->lock);
               }
             }
 
-            if (!main.thread->enabled) {
-              f_thread_unlock(&process_other->active);
+            if (!controller_thread_is_enabled_process(process, main.thread)) {
+              f_thread_unlock(&dependency->active);
 
               break;
             }
 
             if (status_lock != F_signal && F_status_is_error_not(status_lock)) {
-              status_lock = controller_lock_read(main.thread, &main.thread->lock.rule);
+              status_lock = controller_lock_read_process(process, main.thread, &main.thread->lock.rule);
+
               if (status_lock == F_signal || F_status_is_error(status_lock)) {
                 controller_lock_error_critical_print(main.data->error, F_status_set_fine(status_lock), F_false, main.thread);
               }
@@ -2093,8 +2117,8 @@ extern "C" {
 
                 controller_print_unlock_flush(main.data->error.to.stream, &main.thread->lock.print);
 
-                if (!(process_other->options & controller_process_option_simulate)) {
-                  f_thread_unlock(&process_other->active);
+                if (!(dependency->options & controller_process_option_simulate)) {
+                  f_thread_unlock(&dependency->active);
 
                   break;
                 }
@@ -2115,8 +2139,8 @@ extern "C" {
             }
           }
 
-          if (process_other) {
-            f_thread_unlock(&process_other->active);
+          if (dependency) {
+            f_thread_unlock(&dependency->active);
           }
         } // for
 
@@ -2130,14 +2154,14 @@ extern "C" {
       return status;
     }
 
-    if (!main.thread->enabled) {
+    if (!controller_thread_is_enabled_process(process, main.thread)) {
       return F_signal;
     }
 
     if ((process->options & controller_process_option_wait) && F_status_is_error_not(status)) {
-      controller_rule_wait_all(main, F_false, process);
+      status_lock = controller_rule_wait_all_process_type(process->type, main, F_false, process);
 
-      if (!main.thread->enabled) {
+      if (status_lock == F_signal) {
         return F_signal;
       }
     }
@@ -2188,7 +2212,7 @@ extern "C" {
           return status;
         }
 
-        if (!main.thread->enabled) {
+        if (!controller_thread_is_enabled_process(process, main.thread)) {
           return F_signal;
         }
 
@@ -2202,12 +2226,14 @@ extern "C" {
 
     f_thread_unlock(&process->lock);
 
-    status_lock = controller_lock_write(main.thread, &process->lock);
+    status_lock = controller_lock_write_process(process, main.thread, &process->lock);
+
     if (status_lock == F_signal || F_status_is_error(status_lock)) {
       controller_lock_error_critical_print(main.data->error, F_status_set_fine(status_lock), F_false, main.thread);
 
       if (status_lock != F_signal) {
-        status = controller_lock_read(main.thread, &process->lock);
+        status = controller_lock_read_process(process, main.thread, &process->lock);
+
         if (status != F_signal && F_status_is_error_not(status)) {
           return status_lock;
         }
@@ -2223,13 +2249,15 @@ extern "C" {
       process->rule.status = status;
     }
 
-    status_lock = controller_lock_write(main.thread, &main.thread->lock.rule);
+    status_lock = controller_lock_write_process(process, main.thread, &main.thread->lock.rule);
+
     if (status_lock == F_signal || F_status_is_error(status_lock)) {
       controller_lock_error_critical_print(main.data->error, F_status_set_fine(status_lock), F_false, main.thread);
 
       f_thread_unlock(&process->lock);
 
-      status = controller_lock_read(main.thread, &process->lock);
+      status = controller_lock_read_process(process, main.thread, &process->lock);
+
       if (status != F_signal && F_status_is_error_not(status)) {
         return status_lock;
       }
@@ -2262,7 +2290,8 @@ extern "C" {
     f_thread_unlock(&main.thread->lock.rule);
     f_thread_unlock(&process->lock);
 
-    status_lock = controller_lock_read(main.thread, &process->lock);
+    status_lock = controller_lock_read_process(process, main.thread, &process->lock);
+
     if (status_lock == F_signal || F_status_is_error(status_lock)) {
       controller_lock_error_critical_print(main.data->error, F_status_set_fine(status_lock), F_true, main.thread);
 
@@ -2274,9 +2303,9 @@ extern "C" {
 #endif // _di_controller_rule_process_
 
 #ifndef _di_controller_rule_process_begin_
-  f_status_t controller_rule_process_begin(const uint8_t options_force, const f_string_static_t alias_rule, const uint8_t action, const uint8_t options, const f_array_lengths_t stack, const controller_main_t main, const controller_cache_t cache) {
+  f_status_t controller_rule_process_begin(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_main_t main, const controller_cache_t cache) {
 
-    if (!main.thread->enabled) {
+    if (!controller_thread_is_enabled_process_type(type, main.thread)) {
       return F_signal;
     }
 
@@ -2284,7 +2313,8 @@ extern "C" {
 
     controller_process_t *process = 0;
 
-    status = controller_lock_read(main.thread, &main.thread->lock.process);
+    status = controller_lock_read_process_type(type, main.thread, &main.thread->lock.process);
+
     if (status == F_signal || F_status_is_error(status)) {
       controller_lock_error_critical_print(main.data->error, F_status_set_fine(status), F_true, main.thread);
 
@@ -2315,7 +2345,8 @@ extern "C" {
 
       process = main.thread->processs.array[at];
 
-      status = controller_lock_read(main.thread, &process->active);
+      status = controller_lock_read_process_type(type, main.thread, &process->active);
+
       if (status == F_signal || F_status_is_error(status)) {
         controller_lock_error_critical_print(main.data->error, F_status_set_fine(status), F_true, main.thread);
         controller_rule_item_error_print(main.data->error, cache.action, F_false, main.thread);
@@ -2325,7 +2356,8 @@ extern "C" {
         return status;
       }
 
-      status = controller_lock_write(main.thread, &process->lock);
+      status = controller_lock_write_process(process, main.thread, &process->lock);
+
       if (status == F_signal || F_status_is_error(status)) {
         controller_lock_error_critical_print(main.data->error, F_status_set_fine(status), F_false, main.thread);
 
@@ -2348,7 +2380,7 @@ extern "C" {
 
       // the thread is done, so close the thread.
       if (process->state == controller_process_state_done) {
-        f_thread_join(process->id_thread, 0);
+        controller_thread_join(&process->id_thread);
       }
 
       process->id = at;
@@ -2356,7 +2388,8 @@ extern "C" {
 
     f_thread_unlock(&process->lock);
 
-    status = controller_lock_write(main.thread, &process->lock);
+    status = controller_lock_write_process(process, main.thread, &process->lock);
+
     if (status == F_signal || F_status_is_error(status)) {
       controller_lock_error_critical_print(main.data->error, F_status_set_fine(status), F_false, main.thread);
 
@@ -2368,6 +2401,7 @@ extern "C" {
     process->state = controller_process_state_active;
     process->action = action;
     process->options = options;
+    process->type = type;
 
     f_macro_time_spec_t_clear(process->cache.timestamp)
     f_macro_string_range_t_clear(process->cache.range_action)
@@ -2469,7 +2503,8 @@ extern "C" {
 
     // 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) {
-      status_lock = controller_lock_read(main.thread, &process->active);
+      status_lock = controller_lock_read_process(process, main.thread, &process->active);
+
       if (status_lock == F_signal || F_status_is_error(status_lock)) {
         controller_lock_error_critical_print(main.data->error, F_status_set_fine(status_lock), F_true, main.thread);
 
@@ -2477,7 +2512,8 @@ extern "C" {
       }
     }
 
-    status_lock = controller_lock_read(main.thread, &process->lock);
+    status_lock = controller_lock_read_process(process, main.thread, &process->lock);
+
     if (status_lock == F_signal || F_status_is_error(status_lock)) {
       controller_lock_error_critical_print(main.data->error, F_status_set_fine(status_lock), F_true, main.thread);
 
@@ -2494,7 +2530,8 @@ extern "C" {
 
     const f_array_length_t used_original_stack = process->stack.used;
 
-    status_lock = controller_lock_read(main.thread, &main.thread->lock.rule);
+    status_lock = controller_lock_read_process(process, main.thread, &main.thread->lock.rule);
+
     if (status_lock == F_signal || F_status_is_error(status_lock)) {
       controller_lock_error_critical_print(main.data->error, F_status_set_fine(status_lock), F_true, main.thread);
 
@@ -2510,7 +2547,8 @@ extern "C" {
     if (controller_rule_find(process->rule.alias, main.setting->rules, &id_rule) == F_true) {
       f_thread_unlock(&process->lock);
 
-      status_lock = controller_lock_write(main.thread, &process->lock);
+      status_lock = controller_lock_write_process(process, main.thread, &process->lock);
+
       if (status_lock == F_signal || F_status_is_error(status_lock)) {
         controller_lock_error_critical_print(main.data->error, F_status_set_fine(status_lock), F_false, main.thread);
 
@@ -2529,7 +2567,8 @@ extern "C" {
 
       f_thread_unlock(&process->lock);
 
-      status_lock = controller_lock_read(main.thread, &process->lock);
+      status_lock = controller_lock_read_process(process, main.thread, &process->lock);
+
       if (status_lock == F_signal || F_status_is_error(status_lock)) {
         controller_lock_error_critical_print(main.data->error, F_status_set_fine(status_lock), F_true, main.thread);
 
@@ -2548,7 +2587,7 @@ extern "C" {
         controller_entry_error_print(main.data->error, process->cache.action, F_status_set_fine(status), "controller_rule_copy", F_true, main.thread);
       }
       else {
-        for (f_array_length_t i = 0; i < process->stack.used && main.thread->enabled; ++i) {
+        for (f_array_length_t i = 0; i < process->stack.used && controller_thread_is_enabled_process(process, main.thread); ++i) {
 
           if (process->stack.array[i] == id_rule) {
             if (main.data->error.verbosity != f_console_verbosity_quiet) {
@@ -2571,7 +2610,7 @@ extern "C" {
           }
         } // for
 
-        if (!main.thread->enabled) {
+        if (!controller_thread_is_enabled_process(process, main.thread)) {
           f_thread_unlock(&process->lock);
 
           if (options_force & controller_process_option_asynchronous) {
@@ -2590,7 +2629,8 @@ extern "C" {
           else {
             f_thread_unlock(&process->lock);
 
-            status_lock = controller_lock_write(main.thread, &process->lock);
+            status_lock = controller_lock_write_process(process, main.thread, &process->lock);
+
             if (status_lock == F_signal || F_status_is_error(status_lock)) {
               controller_lock_error_critical_print(main.data->error, F_status_set_fine(status_lock), F_false, main.thread);
 
@@ -2605,7 +2645,8 @@ extern "C" {
 
             f_thread_unlock(&process->lock);
 
-            status_lock = controller_lock_read(main.thread, &process->lock);
+            status_lock = controller_lock_read_process(process, main.thread, &process->lock);
+
             if (status_lock == F_signal || F_status_is_error(status_lock)) {
               controller_lock_error_critical_print(main.data->error, F_status_set_fine(status_lock), F_true, main.thread);
 
@@ -2648,7 +2689,8 @@ extern "C" {
       return status;
     }
 
-    status_lock = controller_lock_write(main.thread, &main.thread->lock.rule);
+    status_lock = controller_lock_write_process(process, main.thread, &main.thread->lock.rule);
+
     if (status_lock == F_signal || F_status_is_error(status_lock)) {
       controller_lock_error_critical_print(main.data->error, F_status_set_fine(status_lock), F_false, main.thread);
 
@@ -2678,7 +2720,7 @@ extern "C" {
       f_thread_unlock(&process->lock);
     }
 
-    if (status == F_signal || F_status_set_fine(status) == F_lock && !main.thread->enabled) {
+    if (status == F_signal || F_status_set_fine(status) == F_lock && !controller_thread_is_enabled_process(process, main.thread)) {
       if (options_force & controller_process_option_asynchronous) {
         f_thread_unlock(&process->active);
       }
@@ -2686,7 +2728,8 @@ extern "C" {
       return F_signal;
     }
 
-    status_lock = controller_lock_write(main.thread, &process->lock);
+    status_lock = controller_lock_write_process(process, main.thread, &process->lock);
+
     if (status_lock == F_signal || F_status_is_error(status_lock)) {
       controller_lock_error_critical_print(main.data->error, F_status_set_fine(status_lock), F_false, main.thread);
 
@@ -2717,7 +2760,7 @@ extern "C" {
       f_thread_unlock(&process->active);
     }
 
-    if (main.thread->enabled) {
+    if (controller_thread_is_enabled_process(process, main.thread)) {
       return status;
     }
 
@@ -2726,7 +2769,7 @@ extern "C" {
 #endif // _di_controller_rule_process_do_
 
 #ifndef _di_controller_rule_read_
-  f_status_t controller_rule_read(const f_string_static_t rule_id, controller_main_t main, controller_cache_t *cache, controller_rule_t *rule) {
+  f_status_t controller_rule_read(const bool is_normal, const f_string_static_t rule_id, controller_main_t main, controller_cache_t *cache, controller_rule_t *rule) {
     f_status_t status = F_none;
 
     bool for_item = F_true;
@@ -2820,7 +2863,7 @@ extern "C" {
         controller_error_print(main.data->error, F_status_set_fine(status), "f_string_dynamic_terminate_after", F_true, main.thread);
       }
       else {
-        status = controller_file_load(controller_string_rules_s, rule->alias, controller_string_rule_s, controller_string_rules_length, controller_string_rule_length, main, cache);
+        status = controller_file_load(F_true, controller_string_rules_s, rule->alias, controller_string_rule_s, controller_string_rules_length, controller_string_rule_length, main, cache);
       }
     }
 
@@ -5065,9 +5108,17 @@ extern "C" {
 #endif // _di_controller_rule_validate_
 
 #ifndef _di_controller_rule_wait_all_
-  f_status_t controller_rule_wait_all(const controller_main_t main, const bool required, controller_process_t *caller) {
+  f_status_t controller_rule_wait_all(const bool is_normal, const controller_main_t main, const bool required, controller_process_t *caller) {
+
+    f_status_t status_lock = F_none;
+
+    if (caller) {
+      controller_lock_read_process(caller, main.thread, &main.thread->lock.process);
+    }
+    else {
+      controller_lock_read(is_normal, main.thread, &main.thread->lock.process);
+    }
 
-    f_status_t status_lock = controller_lock_read(main.thread, &main.thread->lock.process);
     if (status_lock == F_signal || F_status_is_error(status_lock)) {
       controller_lock_error_critical_print(main.data->error, F_status_set_fine(status_lock), F_true, main.thread);
 
@@ -5091,19 +5142,39 @@ extern "C" {
 
     bool skip = F_false;
 
-    for (; i < main.thread->processs.used && main.thread->enabled; ++i) {
+    for (; i < main.thread->processs.used; ++i) {
+
+      if (caller) {
+        if (!controller_thread_is_enabled_process(caller, main.thread)) break;
+      }
+      else {
+        if (!controller_thread_is_enabled(is_normal, main.thread)) break;
+      }
 
       process = main.thread->processs.array[i];
 
       if (caller) {
         f_thread_unlock(&main.thread->lock.process);
 
-        status_lock = controller_lock_read(main.thread, &main.thread->lock.rule);
+        if (caller) {
+          status_lock = controller_lock_read_process(caller, main.thread, &main.thread->lock.rule);
+        }
+        else {
+          status_lock = controller_lock_read(is_normal, main.thread, &main.thread->lock.rule);
+        }
+
         if (status_lock == F_signal || F_status_is_error(status_lock)) break;
 
         skip = F_false;
 
-        for (j = 0; j < caller->stack.used && main.thread->enabled; ++j) {
+        for (j = 0; j < caller->stack.used; ++j) {
+
+          if (caller) {
+            if (!controller_thread_is_enabled_process(caller, main.thread)) break;
+          }
+          else {
+            if (!controller_thread_is_enabled(is_normal, main.thread)) break;
+          }
 
           if (main.thread->processs.array[caller->stack.array[j]] && fl_string_dynamic_compare(process->rule.alias, main.thread->processs.array[caller->stack.array[j]]->rule.alias) == F_equal_to) {
             skip = F_true;
@@ -5114,13 +5185,24 @@ extern "C" {
 
         f_thread_unlock(&main.thread->lock.rule);
 
-        status_lock = controller_lock_read(main.thread, &main.thread->lock.process);
-        if (status_lock == F_signal || F_status_is_error(status_lock)) break;
+        if (caller) {
+          status_lock = controller_lock_read_process(caller, main.thread, &main.thread->lock.process);
+        }
+        else {
+          status_lock = controller_lock_read(is_normal, main.thread, &main.thread->lock.process);
+        }
 
+        if (status_lock == F_signal || F_status_is_error(status_lock)) break;
         if (skip) continue;
       }
 
-      status_lock = controller_lock_read(main.thread, &process->active);
+      if (caller) {
+        status_lock = controller_lock_read_process(caller, main.thread, &process->active);
+      }
+      else {
+        status_lock = controller_lock_read(is_normal, main.thread, &process->active);
+      }
+
       if (status_lock == F_signal || F_status_is_error(status_lock)) {
         f_thread_unlock(&main.thread->lock.process);
 
@@ -5129,7 +5211,13 @@ extern "C" {
 
       f_thread_unlock(&main.thread->lock.process);
 
-      status_lock = controller_lock_read(main.thread, &process->lock);
+      if (caller) {
+        status_lock = controller_lock_read_process(caller, main.thread, &process->lock);
+      }
+      else {
+        status_lock = controller_lock_read(is_normal, main.thread, &process->lock);
+      }
+
       if (status_lock == F_signal || F_status_is_error(status_lock)) {
         f_thread_unlock(&process->active);
 
@@ -5141,7 +5229,13 @@ extern "C" {
           f_thread_unlock(&process->lock);
           f_thread_unlock(&process->active);
 
-          status_lock = controller_lock_read(main.thread, &main.thread->lock.process);
+          if (caller) {
+            status_lock = controller_lock_read_process(caller, main.thread, &main.thread->lock.process);
+          }
+          else {
+            status_lock = controller_lock_read(is_normal, main.thread, &main.thread->lock.process);
+          }
+
           if (status_lock == F_signal || F_status_is_error(status_lock)) break;
 
           continue;
@@ -5153,7 +5247,13 @@ extern "C" {
         if (process->state == controller_process_state_done) {
           f_thread_unlock(&process->lock);
 
-          status_lock = controller_lock_write(main.thread, &process->lock);
+          if (caller) {
+            status_lock = controller_lock_write_process(process, main.thread, &process->lock);
+          }
+          else {
+            status_lock = controller_lock_write(is_normal, main.thread, &process->lock);
+          }
+
           if (status_lock == F_signal || F_status_is_error(status_lock)) {
             controller_lock_error_critical_print(main.data->error, F_status_set_fine(status_lock), F_false, main.thread);
 
@@ -5166,15 +5266,21 @@ extern "C" {
             f_thread_unlock(&process->active);
 
             if (f_thread_lock_write_try(&process->active) == F_none) {
-              f_thread_join(process->id_thread, 0);
+
+              controller_thread_join(&process->id_thread);
 
               process->state = controller_process_state_idle;
-              process->id_thread = 0;
 
               f_thread_unlock(&process->active);
             }
 
-            status_lock = controller_lock_read(main.thread, &process->active);
+            if (caller) {
+              status_lock = controller_lock_read_process(caller, main.thread, &process->active);
+            }
+            else {
+              status_lock = controller_lock_read(is_normal, main.thread, &process->active);
+            }
+
             if (status_lock == F_signal || F_status_is_error(status_lock)) {
               f_thread_unlock(&process->lock);
 
@@ -5184,7 +5290,13 @@ extern "C" {
 
           f_thread_unlock(&process->lock);
 
-          status_lock = controller_lock_read(main.thread, &process->lock);
+          if (caller) {
+            status_lock = controller_lock_read_process(caller, main.thread, &process->lock);
+          }
+          else {
+            status_lock = controller_lock_read(is_normal, main.thread, &process->lock);
+          }
+
           if (status_lock == F_signal || F_status_is_error(status_lock)) break;
         }
 
@@ -5195,7 +5307,12 @@ extern "C" {
             f_thread_unlock(&process->lock);
             f_thread_unlock(&process->active);
 
-            status_lock = controller_lock_read(main.thread, &main.thread->lock.process);
+            if (caller) {
+              status_lock = controller_lock_read_process(caller, main.thread, &main.thread->lock.process);
+            }
+            else {
+              status_lock = controller_lock_read(is_normal, main.thread, &main.thread->lock.process);
+            }
 
             break;
           }
@@ -5207,9 +5324,14 @@ extern "C" {
         f_thread_unlock(&process->lock);
         f_thread_unlock(&process->active);
 
-        status_lock = controller_lock_read(main.thread, &main.thread->lock.process);
-        if (status_lock == F_signal || F_status_is_error(status_lock)) break;
+        if (caller) {
+          status_lock = controller_lock_read_process(caller, main.thread, &main.thread->lock.process);
+        }
+        else {
+          status_lock = controller_lock_read(is_normal, main.thread, &main.thread->lock.process);
+        }
 
+        if (status_lock == F_signal || F_status_is_error(status_lock)) break;
         if (F_status_set_fine(status) == F_require) break;
 
         continue;
@@ -5223,13 +5345,24 @@ extern "C" {
         if (status == F_signal) {
           f_thread_unlock(&process->active);
 
-          status_lock = controller_lock_read(main.thread, &main.thread->lock.process);
+          if (caller) {
+            status_lock = controller_lock_read_process(caller, main.thread, &main.thread->lock.process);
+          }
+          else {
+            status_lock = controller_lock_read(is_normal, main.thread, &main.thread->lock.process);
+          }
 
           break;
         }
 
         if (required) {
-          status_lock = controller_lock_read(main.thread, &process->lock);
+          if (caller) {
+            status_lock = controller_lock_read_process(caller, main.thread, &process->lock);
+          }
+          else {
+            status_lock = controller_lock_read(is_normal, main.thread, &process->lock);
+          }
+
           if (status_lock == F_signal || F_status_is_error(status_lock)) {
             f_thread_unlock(&process->active);
 
@@ -5244,7 +5377,12 @@ extern "C" {
 
               f_thread_unlock(&process->active);
 
-              status_lock = controller_lock_read(main.thread, &main.thread->lock.process);
+              if (caller) {
+                status_lock = controller_lock_read_process(caller, main.thread, &main.thread->lock.process);
+              }
+              else {
+                status_lock = controller_lock_read(is_normal, main.thread, &main.thread->lock.process);
+              }
 
               break;
             }
@@ -5260,9 +5398,14 @@ extern "C" {
 
       f_thread_unlock(&process->active);
 
-      status_lock = controller_lock_read(main.thread, &main.thread->lock.process);
-      if (status_lock == F_signal || F_status_is_error(status_lock)) break;
+      if (caller) {
+        status_lock = controller_lock_read_process(caller, main.thread, &main.thread->lock.process);
+      }
+      else {
+        status_lock = controller_lock_read(is_normal, main.thread, &main.thread->lock.process);
+      }
 
+      if (status_lock == F_signal || F_status_is_error(status_lock)) break;
       if (status == F_signal || F_status_set_fine(status) == F_require) break;
     } // for
 
@@ -5274,8 +5417,15 @@ extern "C" {
 
     f_thread_unlock(&main.thread->lock.process);
 
-    if (!main.thread->enabled) {
-      return F_signal;
+    if (caller) {
+      if (!controller_thread_is_enabled_process(caller, main.thread)) {
+        return F_signal;
+      }
+    }
+    else {
+      if (!controller_thread_is_enabled(is_normal, main.thread)) {
+        return F_signal;
+      }
     }
 
     if (status == F_signal || F_status_set_fine(status) == F_require) {
@@ -5290,6 +5440,13 @@ extern "C" {
   }
 #endif // _di_controller_rule_wait_all_
 
+#ifndef _di_controller_rule_wait_all_process_type_
+  f_status_t controller_rule_wait_all_process_type(const uint8_t type, const controller_main_t main, const bool required, controller_process_t *caller) {
+
+    return controller_rule_wait_all(type != controller_process_type_exit, main, required, caller);
+  }
+#endif // _di_controller_rule_wait_all_process_type_
+
 #ifdef __cplusplus
 } // extern "C"
 #endif
index 0923566ca25f5ba90cf97a265941542d1d077f7c..52cb8e3e88a7852109896ddfe0da777237878e89 100644 (file)
@@ -623,6 +623,8 @@ extern "C" {
  *   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.
  * @param stack
  *   A stack representing the processes already running in this rule process dependency tree.
  *   This is used to prevent circular dependencies.
@@ -650,7 +652,7 @@ extern "C" {
  * @see f_thread_create()
  */
 #ifndef _di_controller_rule_process_begin_
-  extern f_status_t controller_rule_process_begin(const uint8_t options_force, const f_string_static_t alias_rule, const uint8_t action, const uint8_t options, const f_array_lengths_t stack, const controller_main_t main, const controller_cache_t cache) f_gcc_attribute_visibility_internal;
+  extern f_status_t controller_rule_process_begin(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_main_t main, const controller_cache_t cache) f_gcc_attribute_visibility_internal;
 #endif // _di_controller_rule_process_begin_
 
 /**
@@ -690,6 +692,9 @@ extern "C" {
 /**
  * Read the rule file, extracting all valid items.
  *
+ * @param is_normal
+ *   If TRUE, then this operates as an entry or control.
+ *   If FALSE, then this operates as an exit.
  * @param rule_id
  *   The string identifying the rule.
  *   This is constructed from the path parts to the file without the file extension and without the settings directory prefix.
@@ -719,7 +724,7 @@ extern "C" {
  * @see fll_fss_basic_list_read().
  */
 #ifndef _di_controller_rule_read_
-  extern f_status_t controller_rule_read(const f_string_static_t rule_id, controller_main_t main, controller_cache_t *cache, controller_rule_t *rule) f_gcc_attribute_visibility_internal;
+  extern f_status_t controller_rule_read(const bool is_normal, const f_string_static_t rule_id, controller_main_t main, controller_cache_t *cache, controller_rule_t *rule) f_gcc_attribute_visibility_internal;
 #endif // _di_controller_rule_read_
 
 /**
@@ -796,6 +801,10 @@ extern "C" {
 /**
  * Wait until all currently running Rule processes are complete.
  *
+ * @param is_normal
+ *   If TRUE, then process as if this is a normal operation (entry and control).
+ *   If FALSE, then process as if this is an exit operation.
+ *   This is ignored when caller is not NULL.
  * @param main
  *   The main data.
  * @param required
@@ -815,9 +824,36 @@ extern "C" {
  *    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_main_t main, const bool required, controller_process_t *process) f_gcc_attribute_visibility_internal;
+  extern f_status_t controller_rule_wait_all(const bool is_normal, const controller_main_t main, const bool required, controller_process_t *process) f_gcc_attribute_visibility_internal;
 #endif // _di_controller_rule_wait_all_
 
+/**
+ * Wait until all currently running Rule processes are complete for some process type.
+ *
+ * @param type
+ *   The process type to use when checking if thread is enabled.
+ * @param main
+ *   The main data.
+ * @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 uint8_t type, const controller_main_t main, const bool required, controller_process_t *caller) f_gcc_attribute_visibility_internal;
+#endif // _di_controller_rule_wait_all_process_type_
+
 #ifdef __cplusplus
 } // extern "C"
 #endif
index 0b36dd46d1029a2adc154dc958d474ecfbc39c7e..bbf273b314afd18f389c7e556d0d18636e67f98d 100644 (file)
@@ -16,22 +16,24 @@ extern "C" {
 
     const controller_main_t *main = (controller_main_t *) arguments;
 
-    if (!main->thread->enabled) return 0;
+    if (main->thread->enabled != controller_thread_enabled) return 0;
 
     const unsigned int interval = main->data->parameters[controller_parameter_test].result == f_console_result_found ? controller_thread_cleanup_interval_short : controller_thread_cleanup_interval_long;
 
     f_status_t status = F_none;
 
-    while (main->thread->enabled) {
+    while (main->thread->enabled == controller_thread_enabled) {
 
       sleep(interval);
 
-      if (main->thread->enabled && f_thread_lock_write_try(&main->thread->lock.process) == F_none) {
+      if (main->thread->enabled != controller_thread_enabled) break;
+
+      if (f_thread_lock_write_try(&main->thread->lock.process) == F_none) {
         controller_process_t *process = 0;
 
         f_array_length_t i = 0;
 
-        for (; i < main->thread->processs.size && main->thread->enabled; ++i) {
+        for (; i < main->thread->processs.size && main->thread->enabled == controller_thread_enabled; ++i) {
 
           if (!main->thread->processs.array[i]) continue;
 
@@ -69,27 +71,28 @@ extern "C" {
 
           // close any still open thread.
           if (process->id_thread) {
-            f_thread_join(process->id_thread, 0);
+            status = f_thread_join(process->id_thread, 0);
 
-            if (!main->thread->enabled) {
-              f_thread_unlock(&process->active);
+            if (F_status_is_error_not(status) || F_status_set_fine(status) == F_found_not) {
+              status = f_thread_lock_write(&process->lock);
 
-              break;
-            }
+              if (F_status_is_error(status)) {
+                controller_lock_error_critical_print(main->data->error, F_status_set_fine(status), F_false, main->thread);
 
-            status = controller_lock_write(main->thread, &process->lock);
-            if (status == F_signal || F_status_is_error(status)) {
-              controller_lock_error_critical_print(main->data->error, F_status_set_fine(status), F_false, main->thread);
+                f_thread_unlock(&process->active);
+                continue;
+              }
 
-              f_thread_unlock(&process->active);
+              process->state = controller_process_state_idle;
+              process->id_thread = 0;
 
-              break;
+              f_thread_unlock(&process->lock);
             }
+            else {
+              f_thread_unlock(&process->active);
 
-            process->state = controller_process_state_idle;
-            process->id_thread = 0;
-
-            f_thread_unlock(&process->lock);
+              continue;
+            }
           }
 
           // deallocate dynamic portions of the structure that are only ever needed while the process is running.
@@ -125,14 +128,39 @@ extern "C" {
 
     controller_main_t *main = (controller_main_t *) arguments;
 
-    if (!main->thread->enabled) return 0;
+    if (main->thread->enabled != controller_thread_enabled) return 0;
 
     return 0;
   }
 #endif // _di_controller_thread_control_
 
+#ifndef _di_controller_thread_is_enabled_
+  f_status_t controller_thread_is_enabled(const bool is_normal, controller_thread_t *thread) {
+
+    if (is_normal) {
+      return thread->enabled == controller_thread_enabled;
+    }
+
+    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 *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 *thread) {
+
+    return controller_thread_is_enabled(type != controller_process_type_exit, thread);
+  }
+#endif // _di_controller_thread_is_enabled_process_type_
+
 #ifndef _di_controller_thread_main_
-  f_status_t controller_thread_main(const f_string_static_t entry_name, controller_data_t *data, controller_setting_t *setting) {
+  f_status_t controller_thread_main(controller_data_t *data, controller_setting_t *setting) {
 
     f_status_t status = F_none;
 
@@ -187,11 +215,11 @@ extern "C" {
           status = F_status_set_error(F_available_not);
         }
       }
-      else {
-        const controller_main_entry_t entry = controller_macro_main_entry_t_initialize(&entry_name, &main, setting);
+      else if (main.setting->name_entry.used) {
 
-        // the entry processing runs using the rule thread.
-        status = f_thread_create(0, &thread.id_rule, &controller_thread_entry, (void *) &entry);
+        const controller_main_entry_t entry = controller_macro_main_entry_t_initialize(&main, main.setting);
+
+        status = f_thread_create(0, &thread.id_entry, &controller_thread_entry, (void *) &entry);
 
         if (F_status_is_error(status)) {
           if (data->error.verbosity != f_console_verbosity_quiet) {
@@ -199,38 +227,20 @@ extern "C" {
           }
         }
         else {
-          if (status == F_child) {
-            controller_thread_delete_simple(&thread);
-
-            return F_child;
-          }
-
-          f_thread_join(thread.id_rule, 0);
+          controller_thread_join(&thread.id_entry);
 
-          thread.id_rule = 0;
           status = thread.status;
         }
       }
     }
 
     // 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_signal && status != F_child && thread.enabled) {
+    if (F_status_is_error_not(status) && status != F_signal && status != F_child && thread.enabled == controller_thread_enabled) {
 
       if (data->parameters[controller_parameter_validate].result == f_console_result_none) {
 
-        if (thread.id_rule) {
-
-          // wait for the entry thread to complete before starting the rule thread.
-          f_thread_join(thread.id_rule, 0);
-
-          if (thread.status == F_child) {
-            controller_thread_delete_simple(&thread);
-
-            return F_child;
-          }
-
-          thread.id_rule = 0;
-        }
+        // 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) {
           status = f_thread_create(0, &thread.id_rule, &controller_thread_rule, (void *) &main);
@@ -267,35 +277,23 @@ extern "C" {
     }
 
     if (F_status_is_error(status) || status == F_signal || !(data->parameters[controller_parameter_validate].result == f_console_result_none || data->parameters[controller_parameter_test].result == f_console_result_found)) {
-
-      if (status != F_signal && thread.id_signal) {
-        f_thread_cancel(thread.id_signal);
-      }
+      // do nothing
     }
     else {
       if (data->parameters[controller_parameter_validate].result == f_console_result_none && setting->mode == controller_setting_mode_service) {
-
-        if (thread.id_signal) {
-          f_thread_join(thread.id_signal, 0);
-
-          thread.id_signal = 0;
-        }
+        controller_thread_join(&thread.id_signal);
       }
       else if (data->parameters[controller_parameter_validate].result == f_console_result_none && setting->mode == controller_setting_mode_program) {
-        controller_rule_wait_all(main, F_false, 0);
-
-        f_thread_cancel(thread.id_signal);
-      }
-      else {
-        f_thread_cancel(thread.id_signal);
+        controller_rule_wait_all(F_true, main, F_false, 0);
       }
     }
 
-    controller_thread_process_cancel(&main);
+    controller_thread_process_cancel(F_false, &main);
 
     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;
@@ -327,7 +325,7 @@ extern "C" {
     {
       controller_thread_t *thread = (controller_thread_t *) process->main_thread;
 
-      if (!thread->enabled) return 0;
+      if (thread->enabled != controller_thread_enabled) return 0;
     }
 
     const f_status_t status = controller_rule_process_do(controller_process_option_asynchronous, process);
@@ -344,7 +342,7 @@ extern "C" {
 
       controller_thread_delete_simple(thread);
       controller_setting_delete_simple(setting);
-      controller_delete_data(data);
+      controller_data_delete(data);
     }
 
     return 0;
@@ -352,17 +350,25 @@ extern "C" {
 #endif // _di_controller_thread_process_
 
 #ifndef _di_controller_thread_process_cancel_
-  void controller_thread_process_cancel(controller_main_t *main) {
+  void controller_thread_process_cancel(const bool by_signal, controller_main_t *main) {
 
     // only cancel when enabled.
-    if (!main->thread->enabled) {
+    if (main->thread->enabled != controller_thread_enabled) {
       return;
     }
 
-    // this must be set, regardless of lock state and only this function changes this.
-    main->thread->enabled = F_false;
+    // 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(&main->thread->lock.alert);
+
+    if (F_status_is_error(status)) {
+      main->thread->enabled = controller_thread_enabled_not;
+    }
+    else {
+      main->thread->enabled = controller_thread_enabled_stop;
+
+      f_thread_mutex_unlock(&main->thread->lock.alert);
+    }
 
-    f_status_t status = F_none;
     f_array_length_t spent = 0;
 
     struct timespec time;
@@ -372,16 +378,89 @@ extern "C" {
     f_array_length_t i = 0;
     pid_t pid = 0;
 
+    // the sleep() function that is run inside the cleanup function must be interrupted via the f_thread_cancel().
+    // @todo consider switching to nanosleep() which may act better with interrupts and not require f_thread_cancel().
     if (main->thread->id_cleanup) {
       f_thread_cancel(main->thread->id_cleanup);
     }
 
-    if (main->thread->id_control) {
-      f_thread_cancel(main->thread->id_control);
+    // the sigtimedwait() function that is run inside of signal must be interrupted via the f_thread_cancel().
+    if (!by_signal && main->thread->id_signal) {
+      f_thread_cancel(main->thread->id_signal);
     }
 
-    if (main->thread->id_rule) {
-      f_thread_cancel(main->thread->id_rule);
+    if (main->thread->enabled == controller_thread_enabled_stop) {
+      if (main->setting->ready == controller_setting_ready_done) {
+
+        // the exit processing runs using the entry thread.
+        if (main->thread->id_entry) {
+          status = f_thread_join(main->thread->id_entry, 0);
+
+          main->thread->id_entry = 0;
+        }
+
+        const controller_main_entry_t entry = controller_macro_main_entry_t_initialize(main, main->setting);
+
+        status = f_thread_create(0, &main->thread->id_entry, &controller_thread_exit, (void *) &entry);
+
+        if (F_status_is_error(status)) {
+          if (main->data->error.verbosity != f_console_verbosity_quiet) {
+            controller_error_print(main->data->error, F_status_set_fine(status), "f_thread_create", F_true, main->thread);
+          }
+
+          if (F_status_is_error_not(f_thread_mutex_lock(&main->thread->lock.alert))) {
+            main->thread->enabled = controller_thread_enabled_not;
+
+            f_thread_mutex_unlock(&main->thread->lock.alert);
+          }
+          else {
+            main->thread->enabled = controller_thread_enabled_not;
+          }
+        }
+        else {
+          struct timespec time;
+
+          do {
+            status = f_thread_mutex_lock(&main->thread->lock.alert);
+
+            if (F_status_is_error(status)) {
+              main->thread->enabled = controller_thread_enabled_not;
+
+              break;
+            }
+            else {
+            }
+
+            controller_time(controller_thread_exit_ready_timeout_seconds, controller_thread_exit_ready_timeout_nanoseconds, &time);
+
+            status = f_thread_condition_wait_timed(&time, &main->thread->lock.alert_condition, &main->thread->lock.alert);
+
+            f_thread_mutex_unlock(&main->thread->lock.alert);
+
+          } while (F_status_is_error_not(status) && main->thread->enabled == controller_thread_enabled_stop);
+
+          if (F_status_is_error(status)) {
+            if (F_status_is_error_not(f_thread_mutex_lock(&main->thread->lock.alert))) {
+              main->thread->enabled = controller_thread_enabled_not;
+
+              f_thread_mutex_unlock(&main->thread->lock.alert);
+            }
+            else {
+              main->thread->enabled = controller_thread_enabled_not;
+            }
+          }
+        }
+      }
+      else {
+        if (F_status_is_error_not(f_thread_mutex_lock(&main->thread->lock.alert))) {
+          main->thread->enabled = controller_thread_enabled_not;
+
+          f_thread_mutex_unlock(&main->thread->lock.alert);
+        }
+        else {
+          main->thread->enabled = controller_thread_enabled_not;
+        }
+      }
     }
 
     for (; i < main->thread->processs.used; ++i) {
@@ -390,6 +469,9 @@ extern "C" {
 
       process = main->thread->processs.array[i];
 
+      // do not cancel exit processes.
+      if (process->type == controller_process_type_exit) continue;
+
       if (process->child > 0) {
         f_signal_send(F_signal_termination, process->child);
       }
@@ -409,6 +491,9 @@ extern "C" {
 
       process = main->thread->processs.array[i];
 
+      // do not cancel exit processes.
+      if (process->type == controller_process_type_exit) continue;
+
       if (process->id_thread) {
         controller_time(0, controller_thread_exit_process_cancel_wait, &time);
 
@@ -418,9 +503,6 @@ extern "C" {
           process->child = 0;
           process->id_thread = 0;
         }
-        else {
-          f_thread_cancel(process->id_thread);
-        }
       }
     } // for
 
@@ -430,6 +512,9 @@ extern "C" {
 
       process = main->thread->processs.array[i];
 
+      // do not cancel exit processes.
+      if (process->type == controller_process_type_exit) continue;
+
       do {
         if (!process->id_thread) break;
 
@@ -484,6 +569,9 @@ extern "C" {
 
       process = main->thread->processs.array[i];
 
+      // do not kill exit processes.
+      if (process->type == controller_process_type_exit) continue;
+
       if (process->id_thread) {
 
         if (process->child > 0) {
@@ -524,23 +612,22 @@ extern "C" {
 
     controller_main_entry_t *entry = (controller_main_entry_t *) arguments;
 
-    if (!entry->main->thread->enabled) return 0;
+    if (!controller_thread_is_enabled(F_true, entry->main->thread)) return 0;
 
     controller_data_t *data = entry->main->data;
     controller_cache_t *cache = &entry->main->thread->cache;
     f_status_t *status = &entry->main->thread->status;
 
-    *status = controller_entry_read(*entry->name, *entry->main, cache);
+    *status = controller_entry_read(F_true, *entry->main, cache);
 
     if (F_status_is_error(*status)) {
       entry->setting->ready = controller_setting_ready_fail;
     }
     else if (*status != F_signal && *status != F_child) {
-      *status = controller_preprocess_entry(*entry->main, cache);
+      *status = controller_preprocess_entry(F_true, *entry->main, cache);
     }
 
     if (F_status_is_error_not(*status) && *status != F_signal && *status != F_child) {
-
       if (data->parameters[controller_parameter_validate].result == f_console_result_none || data->parameters[controller_parameter_test].result == f_console_result_found) {
 
         if (f_file_exists(entry->setting->path_pid.string) == F_true) {
@@ -559,13 +646,13 @@ extern "C" {
           *status = F_status_set_error(F_available_not);
         }
         else {
-          *status = controller_process_entry(F_false, controller_rule_action_type_start, entry->main, cache);
+          *status = controller_process_entry(F_false, F_true, controller_rule_action_type_start, entry->main, cache);
 
           if (F_status_is_error(*status)) {
             entry->setting->ready = controller_setting_ready_fail;
 
             if (F_status_set_fine(*status) == F_require && entry->main->setting->failsafe_enabled) {
-              const f_status_t status_failsafe = controller_process_entry(F_true, controller_rule_action_type_start, entry->main, cache);
+              const f_status_t status_failsafe = controller_process_entry(F_true, F_true, controller_rule_action_type_start, entry->main, cache);
 
               if (F_status_is_error(status_failsafe)) {
                 if (data->error.verbosity != f_console_verbosity_quiet) {
@@ -581,10 +668,10 @@ extern "C" {
               }
             }
           }
-          else if (*status == F_signal || *status == F_child) {
+          else if (*status == F_signal) {
             entry->setting->ready = controller_setting_ready_abort;
           }
-          else {
+          else if (*status != F_child) {
             entry->setting->ready = controller_setting_ready_done;
           }
         }
@@ -599,13 +686,97 @@ extern "C" {
 
       controller_thread_delete_simple(entry->main->thread);
       controller_setting_delete_simple(entry->main->setting);
-      controller_delete_data(entry->main->data);
+      controller_data_delete(entry->main->data);
+
+      return 0;
     }
 
+    f_thread_condition_signal_all(&entry->main->thread->lock.alert_condition);
+
     return 0;
   }
 #endif // _di_controller_thread_entry_
 
+#ifndef _di_controller_thread_exit_
+  void * controller_thread_exit(void *arguments) {
+
+    f_thread_cancel_state_set(PTHREAD_CANCEL_DEFERRED, 0);
+
+    controller_main_entry_t *entry = (controller_main_entry_t *) arguments;
+
+    controller_data_t *data = entry->main->data;
+    controller_cache_t *cache = &entry->main->thread->cache;
+    f_status_t *status = &entry->main->thread->status;
+
+    *status = controller_entry_read(F_false, *entry->main, cache);
+
+    if (F_status_is_error(*status)) {
+      entry->setting->ready = controller_setting_ready_fail;
+    }
+    else if (*status == F_file_found_not) {
+      entry->setting->ready = controller_setting_ready_done;
+    }
+    else if (*status != F_signal && *status != F_child) {
+      *status = controller_preprocess_entry(F_false, *entry->main, cache);
+    }
+
+    if (F_status_is_error_not(*status) && *status != F_signal && *status != F_child && *status != F_file_found_not) {
+      if (data->parameters[controller_parameter_validate].result == f_console_result_none || data->parameters[controller_parameter_test].result == f_console_result_found) {
+
+        *status = controller_process_entry(F_false, F_false, controller_rule_action_type_stop, entry->main, cache);
+
+        if (F_status_is_error(*status)) {
+          entry->setting->ready = controller_setting_ready_fail;
+        }
+        else if (*status == F_signal) {
+          entry->setting->ready = controller_setting_ready_abort;
+        }
+        else if (*status != F_child) {
+          entry->setting->ready = controller_setting_ready_done;
+        }
+      }
+    }
+
+    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->main->thread);
+      controller_setting_delete_simple(entry->main->setting);
+      controller_data_delete(entry->main->data);
+
+      return 0;
+    }
+
+    if (F_status_is_error_not(f_thread_mutex_lock(&entry->main->thread->lock.alert))) {
+      entry->main->thread->enabled = controller_thread_enabled_not;
+
+      f_thread_mutex_unlock(&entry->main->thread->lock.alert);
+    }
+
+    f_thread_condition_signal_all(&entry->main->thread->lock.alert_condition);
+
+    return 0;
+  }
+#endif // _di_controller_thread_exit_
+
+#ifndef _di_controller_thread_join_
+  f_status_t controller_thread_join(f_thread_id_t *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_
+
 #ifndef _di_controller_thread_rule_
   void * controller_thread_rule(void *arguments) {
 
@@ -613,7 +784,7 @@ extern "C" {
 
     controller_main_t *main = (controller_main_t *) arguments;
 
-    if (!main->thread->enabled) return 0;
+    if (!controller_thread_is_enabled(F_true, main->thread)) return 0;
 
     return 0;
   }
@@ -622,26 +793,38 @@ extern "C" {
 #ifndef _di_controller_thread_signal_
   void * controller_thread_signal(void *arguments) {
 
+    f_thread_cancel_state_set(PTHREAD_CANCEL_DEFERRED, 0);
+
     controller_main_t *main = (controller_main_t *) arguments;
 
-    if (!main->thread->enabled) return 0;
+    if (!controller_thread_is_enabled(F_true, main->thread)) return 0;
+
+    siginfo_t information;
+    struct timespec time;
+    int error = 0;
+
+    while (controller_thread_is_enabled(F_true, main->thread)) {
+
+      controller_time(controller_thread_exit_ready_timeout_seconds, controller_thread_exit_ready_timeout_nanoseconds, &time);
 
-    for (int signal = 0; main->thread->enabled; ) {
+      error = sigtimedwait(&main->data->signal.set, &information, &time);
 
-      sigwait(&main->data->signal.set, &signal);
+      if (error == -1) {
+        if (errno == EAGAIN) continue;
+      }
 
       if (main->data->parameters[controller_parameter_interruptable].result == f_console_result_found) {
 
-        if (signal == F_signal_interrupt || signal == F_signal_abort || signal == F_signal_quit || signal == F_signal_termination) {
+        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) {
 
-          main->thread->signal = signal;
+          main->thread->signal = information.si_signo;
 
-          controller_thread_process_cancel(main);
+          controller_thread_process_cancel(F_true, main);
 
           break;
         }
       }
-    } // for
+    } // while
 
     return 0;
   }
index 6dbd0d0f1c5316f4e5d624b37d949ba1c8552a22..6270fc5603c87222a468f4fa731168b5b8754d1b 100644 (file)
@@ -41,10 +41,61 @@ extern "C" {
 #endif // _di_controller_thread_control_
 
 /**
+ * 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 is a normal operation (entry and control).
+ *   If FALSE, then process as if this is 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 *thread) f_gcc_attribute_visibility_internal;
+#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 *thread) f_gcc_attribute_visibility_internal;
+#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 *thread) f_gcc_attribute_visibility_internal;
+#endif // _di_controller_thread_is_enabled_process_type_
+
+/**
  * Start all threads, wait on threads, and handle requests.
  *
- * @param entry_name
- *   The entry name string.
  * @param data
  *   The controller data.
  * @param setting
@@ -57,7 +108,7 @@ extern "C" {
  *   F_failure (with error bit) on any failure.
  */
 #ifndef _di_controller_thread_main_
-  extern f_status_t controller_thread_main(const f_string_static_t entry_name, controller_data_t *data, controller_setting_t *setting) f_gcc_attribute_visibility_internal;
+  extern f_status_t controller_thread_main(controller_data_t *data, controller_setting_t *setting) f_gcc_attribute_visibility_internal;
 #endif // _di_controller_thread_main_
 
 /**
@@ -79,11 +130,14 @@ extern "C" {
 /**
  * Cancel all process threads.
  *
+ * @param by_signal
+ *   If TRUE, this was called from within the signal handling thread, so do not cancel the signal thread.
+ *   If FALSE, then this was not called from within the signal handling thread, so cancel the signal thread.
  * @param main
  *   The main thread data.
  */
 #ifndef _di_controller_thread_process_cancel_
-  void controller_thread_process_cancel(controller_main_t *main) f_gcc_attribute_visibility_internal;
+  void controller_thread_process_cancel(const bool by_signal, controller_main_t *main) f_gcc_attribute_visibility_internal;
 #endif // _di_controller_thread_process_cancel_
 
 /**
@@ -104,6 +158,47 @@ extern "C" {
 #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 *arguments) f_gcc_attribute_visibility_internal;
+#endif // _di_controller_thread_exit_
+
+/***
+ * 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 *id) f_gcc_attribute_visibility_internal;
+#endif // _di_controller_thread_join_
+
+/**
  * Thread for handling rule processing.
  *
  *
diff --git a/level_3/controller/data/settings/example/exits/serial.exit b/level_3/controller/data/settings/example/exits/serial.exit
new file mode 100644 (file)
index 0000000..8d6ee33
--- /dev/null
@@ -0,0 +1,12 @@
+# fss-0005
+
+main:
+  consider serial s_1
+  consider serial s_2
+  consider serial s_3
+  consider serial s_4
+  consider serial s_5
+
+  rule serial s_6
+
+  ready
diff --git a/level_3/controller/data/settings/example/exits/sshd.exit b/level_3/controller/data/settings/example/exits/sshd.exit
new file mode 100644 (file)
index 0000000..73a5a09
--- /dev/null
@@ -0,0 +1,13 @@
+# fss-0005
+
+main:
+  timeout start 7
+  timeout stop 7
+  timeout kill 3
+
+  failsafe boom
+
+  rule service sshd
+
+boom:
+  rule maintenance explode
index af975d1d7a8f7c656cb8fe21eabbf135599f698f..5c45c2e3e8311097019aae90aff6c2ece64267ec 100644 (file)
@@ -9,3 +9,9 @@ script:
     sleep 1
     echo "Serial 1: slept    $(date -u)"
   }
+
+  stop {
+    echo "Serial 1: stopping, sleeping $(date -u)"
+    sleep 1
+    echo "Serial 1: stopping, slept    $(date -u)"
+  }
index be526d7f8cd0d8c866e92c9badf48ac9d012e58c..29c044e7f1a4f26cfb6ac795f73affc40d09db32 100644 (file)
@@ -10,3 +10,9 @@ script:
     sleep 1
     echo "Serial 2: slept    $(date -u)"
   }
+
+  stop {
+    echo "Serial 2: stopping, sleeping $(date -u)"
+    sleep 1
+    echo "Serial 2: stopping, slept    $(date -u)"
+  }
index 124de75025bfa97c63e6aabedc9fa575a41ebc69..3960d115ae58c12e3f08202bc3dd28670bff4953 100644 (file)
@@ -10,3 +10,9 @@ script:
     sleep 1
     echo "Serial 3: slept    $(date -u)"
   }
+
+  stop {
+    echo "Serial 3: stopping, sleeping $(date -u)"
+    sleep 1
+    echo "Serial 3: stopping, slept    $(date -u)"
+  }
index beb52f7f225bb36d79d5d3eb001a2bc94a78d94f..34f4ee9b977f88dd5faff5be8378698d1e8c2bd6 100644 (file)
@@ -10,3 +10,9 @@ script:
     sleep 1
     echo "Serial 4: slept    $(date -u)"
   }
+
+  stop {
+    echo "Serial 4: stopping, sleeping $(date -u)"
+    sleep 1
+    echo "Serial 4: stopping, slept    $(date -u)"
+  }
index eb6aab82f6500b10c1809bf9e263465b76d06e13..5dde94d6826d2d3018eeb4e94b654cf500960df2 100644 (file)
@@ -10,3 +10,9 @@ script:
     sleep 1
     echo "Serial 5: slept    $(date -u)"
   }
+
+  stop {
+    echo "Serial 5: stopping, sleeping $(date -u)"
+    sleep 1
+    echo "Serial 5: stopping, slept    $(date -u)"
+  }
index af3bc822d7aa8dc277bbe3e2cc9b2e5b60518f0a..fda3365c0d2a18e8a69fe91793ebcd904546590c 100644 (file)
@@ -10,3 +10,9 @@ script:
     sleep 1
     echo "Serial 6: slept    $(date -u)"
   }
+
+  stop {
+    echo "Serial 6: stopping, sleeping $(date -u)"
+    sleep 1
+    echo "Serial 6: stopping, slept    $(date -u)"
+  }
index 5a5f3b6e673b4f81322fd25ff6d0a2acc51153e8..4c3d4d676b1c5e9ccddbc70171209bbc68bcfa87 100644 (file)
@@ -12,3 +12,4 @@ service:
   pid_file /var/run/sshd.pid
   with full_path
   start sshd
+  #stop