]> Kevux Git Server - fll/commitdiff
Bugfix: PID and PID file should account for multiples during process execution.
authorKevin Day <thekevinday@gmail.com>
Tue, 27 Apr 2021 23:49:35 +0000 (18:49 -0500)
committerKevin Day <thekevinday@gmail.com>
Tue, 27 Apr 2021 23:49:35 +0000 (18:49 -0500)
I forgot all about needing to do this so I am considering this a bug.

Each process may execute multiple Actions.
Each Action has its own PID.

In the case of foreground (synchronous) execution, having only a single PID and PID file path on the Process structure is not a problem.
When with PID file (asynchronous) execution operates, multiple PIDs (and respective PID files) may exist for any single Process structure.

Use an array of PIDs and PID file paths.

level_3/controller/c/private-common.c
level_3/controller/c/private-common.h
level_3/controller/c/private-rule.c
level_3/controller/c/private-thread.c
level_3/controller/data/settings/example/entries/utility.entry
level_3/controller/data/settings/example/rules/utility/sleeper_2.rule
level_3/controller/data/settings/example/rules/utility/sleeper_3.rule [new file with mode: 0644]

index 36db682ab0442fb5917863b32a5136dff0084765..75043e9f860ab1bd9895b6b0ae9bb00f471750ca 100644 (file)
@@ -314,6 +314,46 @@ extern "C" {
   }
 #endif // _di_controller_lock_write_process_type_
 
+#ifndef _di_controller_pids_increase_
+  f_status_t controller_pids_increase(controller_pids_t *pids) {
+
+    if (pids->used + 1 > pids->size) {
+      f_array_length_t size = pids->used + controller_default_allocation_step;
+
+      if (size > f_array_length_t_size) {
+        if (pids->used + 1 > f_array_length_t_size) {
+          return F_status_set_error(F_array_too_large);
+        }
+
+        size = f_array_length_t_size;
+      }
+
+      return controller_pids_resize(size, pids);
+    }
+
+    return F_data_not;
+  }
+#endif // _di_controller_pids_increase_
+
+#ifndef _di_controller_pids_resize_
+  f_status_t controller_pids_resize(const f_array_length_t length, controller_pids_t *pids) {
+
+    f_status_t status = F_none;
+
+    status = f_memory_resize(pids->size, length, sizeof(controller_rule_t), (void **) & pids->array);
+
+    if (F_status_is_error_not(status)) {
+      pids->size = length;
+
+      if (pids->used > pids->size) {
+        pids->used = length;
+      }
+    }
+
+    return status;
+  }
+#endif // _di_controller_pids_resize_
+
 #ifndef _di_controller_print_unlock_flush_
   void controller_print_unlock_flush(FILE * const stream, f_thread_mutex_t *mutex) {
 
@@ -343,9 +383,10 @@ extern "C" {
     controller_lock_delete_mutex(&process->wait_lock);
 
     controller_cache_delete_simple(&process->cache);
+    controller_pids_resize(0, &process->childs);
     controller_rule_delete_simple(&process->rule);
 
-    f_string_dynamic_resize(0, &process->path_pid);
+    f_string_dynamics_resize(0, &process->path_pids);
 
     f_macro_array_lengths_t_delete_simple(process->stack)
   }
index 70acb225d226696c907a2c9ed6986e6c8f2c5262..193447f4714cc8f48364a6df14a6e69c310bf55b 100644 (file)
@@ -878,6 +878,28 @@ extern "C" {
 #endif // _di_controller_with_defines_
 
 /**
+ * An array of PIDs.
+ *
+ * array: An array of rule PIDs.
+ * size:  Total amount of allocated space.
+ * used:  Total number of allocated spaces used.
+ */
+#ifndef _di_controller_pids_t_
+  typedef struct {
+    pid_t *array;
+
+    f_array_length_t size;
+    f_array_length_t used;
+  } controller_pids_t;
+
+  #define controller_pids_t_initialize { \
+    0, \
+    0, \
+    0, \
+  }
+#endif // _di_controller_pids_t_
+
+/**
  * A Rule Process.
  *
  * This refers to "process" as in the processing of a single rule for the given Rule ID and does not refer to "process" as in a CPU Process.
@@ -941,7 +963,6 @@ extern "C" {
     uint8_t action;
     uint8_t options;
     uint8_t type;
-    pid_t child;
 
     f_thread_id_t id_thread;
     f_thread_lock_t lock;
@@ -952,8 +973,9 @@ extern "C" {
     controller_cache_t cache;
     f_array_lengths_t stack;
 
-    f_string_dynamic_t path_pid;
+    f_string_dynamics_t path_pids;
 
+    controller_pids_t childs;
     controller_rule_t rule;
 
     void *main_data;
@@ -967,14 +989,14 @@ extern "C" {
     0, \
     0, \
     0, \
-    0, \
     f_thread_id_t_initialize, \
     f_thread_lock_t_initialize, \
     f_thread_lock_t_initialize, \
     f_thread_condition_t_initialize, \
     controller_cache_t_initialize, \
     f_array_lengths_t_initialize, \
-    f_string_dynamic_t_initialize, \
+    f_string_dynamics_t_initialize, \
+    controller_pids_t_initialize, \
     controller_rule_t_initialize, \
     0, \
     0, \
@@ -1811,6 +1833,49 @@ extern "C" {
 #endif // _di_controller_lock_write_process_type_
 
 /**
+ * Increase the size of the pid array, but only if necessary.
+ *
+ * If the given length is too large for the buffer, then attempt to set max buffer size (f_array_length_t_size).
+ * If already set to the maximum buffer size, then the resize will fail.
+ *
+ * @param pids
+ *   The pid array to resize.
+ *
+ * @return
+ *   F_none on success.
+ *   F_data_not on success, but there is no reason to increase size (used + controller_default_allocation_step <= size).
+ *
+ *   F_array_too_large (with error bit) if the new array length is too large.
+ *   F_memory_not (with error bit) on out of memory.
+ *   F_parameter (with error bit) if a parameter is invalid.
+ *
+ * @see controller_pids_resize()
+ */
+#ifndef _di_controller_pids_increase_
+  extern f_status_t controller_pids_increase(controller_pids_t *pids) f_gcc_attribute_visibility_internal;
+#endif // _di_controller_rule_increase_
+
+/**
+ * Resize the pid array.
+ *
+ * @param length
+ *   The new size to use.
+ * @param pids
+ *   The pid array to resize.
+ *
+ * @return
+ *   F_none on success.
+ *
+ *   F_memory_not (with error bit) on out of memory.
+ *   F_parameter (with error bit) if a parameter is invalid.
+ *
+ * @see f_memory_resize()
+ */
+#ifndef _di_controller_pids_resize_
+  extern f_status_t controller_pids_resize(const f_array_length_t length, controller_pids_t *pids) f_gcc_attribute_visibility_internal;
+#endif // _di_controller_pids_resize_
+
+/**
  * Flush the stream buffer and then unlock the mutex.
  *
  * Weird behavior was observed when piping data from this program.
index 4879e7be11922514d3ad1058051e49378fbaed16..3e94324f9921f9378e7e6baced6a34cc4bd4c0f4 100644 (file)
@@ -1098,6 +1098,31 @@ extern "C" {
     int result = 0;
     pid_t id_child = 0;
 
+    status = controller_pids_increase(&process->childs);
+
+    if (F_status_is_error(status)) {
+      fll_error_print(main.data->error, F_status_set_fine(status), "controller_pids_increase", F_true);
+
+      return status;
+    }
+
+    pid_t *child = 0;
+    f_string_dynamic_t *child_pid_file = 0;
+
+    {
+      f_array_length_t i = 0;
+
+      for (; i < process->childs.used && process->childs.array[i]; ++i) {
+        // do nothing
+      } // for
+
+      child = &process->childs.array[i];
+
+      if (i == process->childs.used) {
+        ++process->childs.used;
+      }
+    }
+
     if (options & controller_process_option_simulate) {
       if (main.data->error.verbosity != f_console_verbosity_quiet) {
         f_thread_mutex_lock(&main.thread->lock.print);
@@ -1153,7 +1178,7 @@ extern "C" {
       }
 
       // assign the child process id to allow for the cancel process to send appropriate termination signals to the child process.
-      process->child = id_child;
+      *child = id_child;
 
       f_thread_unlock(&process->lock);
 
@@ -1198,7 +1223,7 @@ extern "C" {
       }
 
       // remove the pid now that waidpid() has returned.
-      process->child = 0;
+      *child = 0;
 
       f_thread_unlock(&process->lock);
 
@@ -1276,7 +1301,48 @@ extern "C" {
     int result = 0;
     pid_t id_child = 0;
 
-    process->path_pid.used = 0;
+    status = controller_pids_increase(&process->childs);
+
+    if (F_status_is_error(status)) {
+      fll_error_print(main.data->error, F_status_set_fine(status), "controller_pids_increase", F_true);
+
+      return status;
+    }
+
+    status = f_string_dynamics_increase(&process->path_pids);
+
+    if (F_status_is_error(status)) {
+      fll_error_print(main.data->error, F_status_set_fine(status), "f_string_dynamics_increase", F_true);
+
+      return status;
+    }
+
+    pid_t *child = 0;
+    f_string_dynamic_t *child_pid_file = 0;
+
+    {
+      f_array_length_t i = 0;
+
+      for (; i < process->childs.used && process->childs.array[i]; ++i) {
+        // do nothing
+      } // for
+
+      child = &process->childs.array[i];
+
+      if (i == process->childs.used) {
+        ++process->childs.used;
+      }
+
+      for (i = 0; i < process->path_pids.used && process->path_pids.array[i].used; ++i) {
+        // do nothing
+      } // for
+
+      child_pid_file = &process->path_pids.array[i];
+
+      if (i == process->path_pids.used) {
+        ++process->path_pids.used;
+      }
+    }
 
     status = f_file_exists(pid_file.string);
 
@@ -1287,12 +1353,12 @@ extern "C" {
     }
 
     if (status == F_true) {
-      fll_error_file_print(main.data->error, F_file_found, "f_file_exists", F_true, pid_file.string, "create PID", fll_error_file_type_file);
+      fll_error_file_print(main.data->error, F_file_found, "f_file_exists", F_true, pid_file.string, "find", fll_error_file_type_file);
 
       return F_status_set_error(F_file_found);
     }
 
-    status = controller_string_dynamic_append_terminated(pid_file, &process->path_pid);
+    status = controller_string_dynamic_append_terminated(pid_file, child_pid_file);
 
     if (F_status_is_error(status)) {
       fll_error_print(main.data->error, F_status_set_fine(status), "controller_string_dynamic_append_terminated", F_true);
@@ -1355,7 +1421,7 @@ extern "C" {
       }
 
       // assign the child process id to allow for the cancel process to send appropriate termination signals to the child process.
-      process->child = id_child;
+      *child = id_child;
 
       f_thread_unlock(&process->lock);
 
@@ -1400,7 +1466,7 @@ extern "C" {
       }
 
       // remove the pid now that waidpid() has returned.
-      process->child = 0;
+      *child = 0;
 
       f_thread_unlock(&process->lock);
 
index d619c959c29c7d29b5262dc13f57cb72eab4a365..bee50e6d50a289171d27f4cc810b3926f33b7896 100644 (file)
@@ -59,12 +59,20 @@ extern "C" {
             continue;
           }
 
-          // if process has a pid file, then it is running in the background, only cleanup if the pid file no longer exists.
-          if (process->path_pid.used && f_file_exists(process->path_pid.string) == F_true) {
-            f_thread_unlock(&process->active);
-            f_thread_unlock(&process->lock);
+          // if process has a PID file, then it is running in the background, only cleanup if the PID file no longer exists.
+          if (process->path_pids.used) {
+            f_array_length_t j = 0;
 
-            continue;
+            for (; j < process->path_pids.used; ++j) {
+              if (process->path_pids.array[j].used && f_file_exists(process->path_pids.array[j].string) == F_true) break;
+            } // for
+
+            if (j < process->path_pids.used) {
+              f_thread_unlock(&process->active);
+              f_thread_unlock(&process->lock);
+
+              continue;
+            }
           }
 
           f_thread_unlock(&process->lock);
@@ -103,10 +111,21 @@ extern "C" {
           controller_cache_delete_simple(&process->cache);
           f_type_array_lengths_resize(0, &process->stack);
 
-          // deallocate the pid file.
-          if (process->path_pid.used) {
-            process->path_pid.used = 0;
-            f_string_dynamic_resize(0, &process->path_pid);
+          // shrink the childs array.
+          if (process->childs.used) {
+            for (; process->childs.used; --process->childs.used) {
+              if (process->childs.array[process->childs.used]) break;
+            } // for
+
+            if (process->childs.used < process->childs.size) {
+              controller_pids_resize(process->childs.used, &process->childs);
+            }
+          }
+
+          // deallocate the PID files.
+          if (process->path_pids.used) {
+            process->path_pids.used = 0;
+            f_string_dynamics_resize(0, &process->path_pids);
           }
 
           // deallocate any rules in the space that is declared to be unused.
@@ -408,6 +427,7 @@ extern "C" {
     controller_process_t *process = 0;
 
     f_array_length_t i = 0;
+    f_array_length_t j = 0;
     pid_t pid = 0;
 
     // the sleep() function that is run inside the cleanup function must be interrupted via the f_thread_cancel().
@@ -437,39 +457,23 @@ extern "C" {
       // do not cancel exit processes, when not performing "execute" during exit.
       if (process->type == controller_process_type_exit && main->thread->enabled != controller_thread_enabled_exit_execute) continue;
 
-      if (process->child > 0) {
-        f_signal_send(F_signal_termination, process->child);
-      }
-
-      if (process->path_pid.used && f_file_exists(process->path_pid.string) == F_true) {
-        status = controller_file_pid_read(process->path_pid, &pid);
+      for (j = 0; j < process->childs.used; ++j) {
 
-        if (pid) {
-          f_signal_send(F_signal_termination, pid);
+        if (process->childs.array[j] > 0) {
+          f_signal_send(F_signal_termination, process->childs.array[j]);
         }
-      }
-    } // for
-
-    for (i = 0; i < main->thread->processs.used; ++i) {
-
-      if (!main->thread->processs.array[i]) continue;
-      if (caller && i == caller->id) continue;
-
-      process = main->thread->processs.array[i];
-
-      // do not cancel exit processes, when not performing "execute" during exit.
-      if (process->type == controller_process_type_exit && main->thread->enabled != controller_thread_enabled_exit_execute) continue;
+      } // for
 
-      if (process->id_thread) {
-        controller_time(0, controller_thread_exit_process_cancel_wait, &time);
+      for (j = 0; j < process->path_pids.used; ++j) {
 
-        status = f_thread_join_timed(process->id_thread, time, 0);
+        if (process->path_pids.array[j].used && f_file_exists(process->path_pids.array[j].string) == F_true) {
+          status = controller_file_pid_read(process->path_pids.array[j], &pid);
 
-        if (status == F_none) {
-          process->child = 0;
-          process->id_thread = 0;
+          if (pid) {
+            f_signal_send(F_signal_termination, pid);
+          }
         }
-      }
+      } // for
     } // for
 
     for (i = 0; i < main->thread->processs.size && spent < controller_thread_exit_process_cancel_total; ++i) {
@@ -490,7 +494,11 @@ extern "C" {
         status = f_thread_join_timed(process->id_thread, time, 0);
 
         if (status == F_none) {
-          process->child = 0;
+          for (j = 0; j < process->childs.size; ++j) {
+            process->childs.array[j] = 0;
+          } // for
+
+          process->childs.used = 0;
           process->id_thread = 0;
         }
 
@@ -498,35 +506,33 @@ extern "C" {
 
       } while (status == F_time && spent < controller_thread_exit_process_cancel_total);
 
-      if (process->path_pid.used) {
-        for (; spent < controller_thread_exit_process_cancel_total; ++spent) {
+      if (process->path_pids.used) {
+        for (j = 0; j < process->path_pids.used; ++j) {
 
-          if (f_file_exists(process->path_pid.string) == F_true) {
-            status = controller_file_pid_read(process->path_pid, &pid);
+          for (; spent < controller_thread_exit_process_cancel_total; ++spent) {
 
-            if (pid) {
+            if (process->path_pids.array[j].used && f_file_exists(process->path_pids.array[j].string) == F_true) {
+              status = controller_file_pid_read(process->path_pids.array[j], &pid);
 
-              // a hackish way to determine if the pid exists while waiting.
-              if (getpgid(pid) >= 0) {
-                time.tv_sec = 0;
-                time.tv_nsec = controller_thread_exit_process_cancel_wait;
+              if (pid) {
 
-                nanosleep(&time, 0);
-              }
-              else {
-                f_file_remove(process->path_pid.string);
-                process->path_pid.used = 0;
+                // a hackish way to determine if the pid exists while waiting.
+                if (getpgid(pid) >= 0) {
+                  time.tv_sec = 0;
+                  time.tv_nsec = controller_thread_exit_process_cancel_wait;
 
-                break;
+                  nanosleep(&time, 0);
+                  continue;
+                }
+                else {
+                  f_file_remove(process->path_pids.array[j].string);
+                  process->path_pids.array[j].used = 0;
+                }
               }
             }
-            else {
-              break;
-            }
-          }
-          else {
+
             break;
-          }
+          } // for
         } // for
       }
     } // for
@@ -542,34 +548,48 @@ extern "C" {
       if (process->type == controller_process_type_exit && main->thread->enabled != controller_thread_enabled_exit_execute) continue;
 
       if (process->id_thread) {
+        if (process->childs.used) {
+          for (j = 0; j < process->childs.used; ++j) {
 
-        if (process->child > 0) {
-          f_signal_send(F_signal_kill, process->child);
+            if (process->childs.array[j] > 0) {
+              f_signal_send(F_signal_kill, process->childs.array[j]);
 
-          time.tv_sec = 0;
-          time.tv_nsec = controller_thread_exit_process_cancel_wait;
+              time.tv_sec = 0;
+              time.tv_nsec = controller_thread_exit_process_cancel_wait;
+
+              process->childs.array[j] = 0;
+            }
+          } // for
 
           nanosleep(&time, 0);
         }
 
         f_thread_join(process->id_thread, 0);
 
-        process->child = 0;
         process->id_thread = 0;
       }
 
-      if (process->path_pid.used) {
-        if (f_file_exists(process->path_pid.string) == F_true) {
-          status = controller_file_pid_read(process->path_pid, &pid);
+      for (j = 0; j < process->childs.size; ++j) {
+        process->childs.array[j] = 0;
+      } // for
+
+      process->childs.used = 0;
+
+      for (j = 0; j < process->path_pids.used; ++j) {
+
+        if (f_file_exists(process->path_pids.array[j].string) == F_true) {
+          status = controller_file_pid_read(process->path_pids.array[j], &pid);
 
           if (pid) {
             f_signal_send(F_signal_kill, pid);
           }
 
-          f_file_remove(process->path_pid.string);
-          process->path_pid.used = 0;
+          f_file_remove(process->path_pids.array[j].string);
+          process->path_pids.array[j].used = 0;
         }
-      }
+      } // for
+
+      process->path_pids.used = 0;
     } // for
   }
 #endif // _di_controller_thread_process_cancel_
index 2f7fda29d4b796faa6753e39d817b5ee51ecde97..ebd36ef6bce096730a6fdb978e2fbe990409a4c2 100644 (file)
@@ -9,6 +9,7 @@ main:
 
   start utility sleeper_1
   start utility sleeper_2
+  start utility sleeper_3
 
 explode:
   start maintenance boom
index be70fe105347769fb81ce2a12208c567022e6290..27b0a0a9430a5352a53121d39f62ac1f38d79185 100644 (file)
@@ -12,7 +12,7 @@ utility:
 
     main() {
       if [[ -f /tmp/sleeper_2.pid ]] ; then
-        echo "Failure: pid file '/tmp/sleeper_1.pid' already exists."
+        echo "Failure: pid file '/tmp/sleeper_2.pid' already exists."
         return 1
       fi
 
diff --git a/level_3/controller/data/settings/example/rules/utility/sleeper_3.rule b/level_3/controller/data/settings/example/rules/utility/sleeper_3.rule
new file mode 100644 (file)
index 0000000..550c716
--- /dev/null
@@ -0,0 +1,54 @@
+# fss-000d
+# sleeper rule whose program creates its own PID file, runs in the background, sleep for a while, removes PID file, and returns.
+
+setting:
+  name "Sleeper #2"
+  nice 10
+
+utility:
+  pid_file /tmp/sleeper_3.1.pid
+  start {
+    \#!/bin/bash
+
+    main() {
+      if [[ -f /tmp/sleeper_3.1.pid ]] ; then
+        echo "Failure: pid file '/tmp/sleeper_3.1.pid' already exists."
+        return 1
+      fi
+
+      echo "$BASHPID" > /tmp/sleeper_3.1.pid
+
+      echo "Sleeper 3.1, now sleeping."
+      sleep 15
+
+      echo "Sleeper 3.1, done sleeping."
+      rm -f /tmp/sleeper_3.1.pid
+      return 0
+    \}
+
+    main &
+  }
+
+utility:
+  pid_file /tmp/sleeper_3.2.pid
+  start {
+    \#!/bin/bash
+
+    main() {
+      if [[ -f /tmp/sleeper_3.2.pid ]] ; then
+        echo "Failure: pid file '/tmp/sleeper_3.2.pid' already exists."
+        return 1
+      fi
+
+      echo "$BASHPID" > /tmp/sleeper_3.2.pid
+
+      echo "Sleeper 3.2, now sleeping."
+      sleep 20
+
+      echo "Sleeper 3.2, done sleeping."
+      rm -f /tmp/sleeper_3.2.pid
+      return 0
+    \}
+
+    main &
+  }