From: Kevin Day Date: Tue, 27 Apr 2021 23:49:35 +0000 (-0500) Subject: Bugfix: PID and PID file should account for multiples during process execution. X-Git-Tag: 0.5.3~13 X-Git-Url: https://git.kevux.org/?a=commitdiff_plain;h=d3ffd809385da9a9b576fc49cb5f19cd6eebd44f;p=fll Bugfix: PID and PID file should account for multiples during process execution. 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. --- diff --git a/level_3/controller/c/private-common.c b/level_3/controller/c/private-common.c index 36db682..75043e9 100644 --- a/level_3/controller/c/private-common.c +++ b/level_3/controller/c/private-common.c @@ -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) } diff --git a/level_3/controller/c/private-common.h b/level_3/controller/c/private-common.h index 70acb22..193447f 100644 --- a/level_3/controller/c/private-common.h +++ b/level_3/controller/c/private-common.h @@ -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. diff --git a/level_3/controller/c/private-rule.c b/level_3/controller/c/private-rule.c index 4879e7b..3e94324 100644 --- a/level_3/controller/c/private-rule.c +++ b/level_3/controller/c/private-rule.c @@ -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); diff --git a/level_3/controller/c/private-thread.c b/level_3/controller/c/private-thread.c index d619c95..bee50e6 100644 --- a/level_3/controller/c/private-thread.c +++ b/level_3/controller/c/private-thread.c @@ -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_ diff --git a/level_3/controller/data/settings/example/entries/utility.entry b/level_3/controller/data/settings/example/entries/utility.entry index 2f7fda2..ebd36ef 100644 --- a/level_3/controller/data/settings/example/entries/utility.entry +++ b/level_3/controller/data/settings/example/entries/utility.entry @@ -9,6 +9,7 @@ main: start utility sleeper_1 start utility sleeper_2 + start utility sleeper_3 explode: start maintenance boom diff --git a/level_3/controller/data/settings/example/rules/utility/sleeper_2.rule b/level_3/controller/data/settings/example/rules/utility/sleeper_2.rule index be70fe1..27b0a0a 100644 --- a/level_3/controller/data/settings/example/rules/utility/sleeper_2.rule +++ b/level_3/controller/data/settings/example/rules/utility/sleeper_2.rule @@ -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 index 0000000..550c716 --- /dev/null +++ b/level_3/controller/data/settings/example/rules/utility/sleeper_3.rule @@ -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 & + }