From: Kevin Day Date: Wed, 13 Oct 2021 23:21:44 +0000 (-0500) Subject: Update: Implement "rerun" and properly get failed execute status codes. X-Git-Tag: 0.5.6~16 X-Git-Url: https://git.kevux.org/?a=commitdiff_plain;h=b72b3ffcb34b85de747e279a5765e8b8811b05f9;p=fll Update: Implement "rerun" and properly get failed execute status codes. Provide "rerun" support feature. I discovered some problems with the execute functions after testing the "rerun" feature. Specifically, when a child exits, the exit code is not properly propogating to the caller. This makes it impossible to detect a failed execution. It turns out that I need to call exit() with the appropriate failed code (which is only 8-bits). The pthread_exit() is documented as always exit() with a value of 0. This makes it impossible to communicate the failed state to the parent via an exit return code. I am forced to call exit() here. To do this, several significant changes are required. Implement F_execute_codes enum and related functions to handle the 8-bit large status codes. The limited set of codes are focused on possible failure states from the execute functions. Functions to convert to and from regular status codes and these special limited execute status codes are now provided. A union called f_execute_result_t is now provided as a way for the execute functions to return a pid_t for the parent process and an int for the child process. The int in this case is intended to hold the special execute status codes. An int is used rather than a more appropriate uint8_t because the standard POSIX functions use int. Additional normal status codes F_too_large and F_too_small are now provided as generic too large and too small statuses. Exit calls now directly use the execute status codes when calling exit(). The controller program child process now returns the exit status code either at the end of the appropraite threads or at the end of the main(). A new micro time conversion function is provided so that the deprecated usleep() can be avoided. I may end up rewriting this to perform the sleep as well rather than just return a timespec. Yet another htop rule is provided for testing "rerun". See the previous commit for additional details (5b09409e3c7b5eafc164405c5d487bdfd083be11). --- diff --git a/build/level_0/settings b/build/level_0/settings index 23b3614..2ad4bd1 100644 --- a/build/level_0/settings +++ b/build/level_0/settings @@ -22,7 +22,7 @@ build_language c build_libraries -lc -lcap build_libraries-level build_libraries-level_threadless -build_sources_library account.c private-account.c capability.c color.c color-common.c console.c console-common.c control_group.c control_group-common.c conversion.c conversion-common.c private-conversion.c directory.c private-directory.c environment.c private-environment.c file.c file-common.c private-file.c fss.c private-fss.c fss-common.c fss_named.c fss_nest.c fss_set.c iki.c iki-common.c private-iki.c limit.c memory.c memory_structure.c private-memory.c path.c path-common.c private-path.c pipe.c print.c print-common.c private-print.c serialize.c serialize-common.c private-serialize.c signal.c socket.c string.c string-common.c private-string.c string_dynamic.c string_map.c string_quantity.c string_range.c string_triple.c type_array.c private-type_array.c utf.c utf-common.c private-utf.c utf_dynamic.c utf_map.c utf_triple.c +build_sources_library account.c private-account.c capability.c color.c color-common.c console.c console-common.c control_group.c control_group-common.c conversion.c conversion-common.c private-conversion.c directory.c private-directory.c environment.c private-environment.c execute.c file.c file-common.c private-file.c fss.c private-fss.c fss-common.c fss_named.c fss_nest.c fss_set.c iki.c iki-common.c private-iki.c limit.c memory.c memory_structure.c private-memory.c path.c path-common.c private-path.c pipe.c print.c print-common.c private-print.c serialize.c serialize-common.c private-serialize.c signal.c socket.c string.c string-common.c private-string.c string_dynamic.c string_map.c string_quantity.c string_range.c string_triple.c type_array.c private-type_array.c utf.c utf-common.c private-utf.c utf_dynamic.c utf_map.c utf_triple.c build_sources_library-level thread.c private-thread.c build_sources_program build_sources_headers account.h account-common.h capability.h capability-common.h color.h color-common.h console.h console-common.h control_group.h control_group-common.h conversion.h conversion-common.h directory.h directory_type.h directory-common.h environment.h environment-common.h execute.h execute-common.h file.h file-common.h fss.h fss-common.h fss_comment.h fss_delimit.h fss_named.h fss_nest.h fss_quote.h fss_set.h iki.h iki-common.h limit.h limit-common.h memory.h memory_structure.h memory-common.h path.h path-common.h pipe.h print.h print-common.h serialize.h serialize-common.h signal.h signal-common.h socket.h socket-common.h status.h string.h string-common.h string_dynamic.h string_map.h string_quantity.h string_range.h string_triple.h type.h type_array.h type_array-common.h utf.h utf-common.h utf_dynamic.h utf_map.h utf_triple.h diff --git a/build/monolithic/settings b/build/monolithic/settings index 4a04104..7576bb0 100644 --- a/build/monolithic/settings +++ b/build/monolithic/settings @@ -22,7 +22,7 @@ build_language c build_libraries -lc -lcap build_libraries-monolithic build_libraries-monolithic_threadless -build_sources_library level_0/account.c level_0/private-account.c level_0/capability.c level_0/color.c level_0/color-common.c level_0/console.c level_0/console-common.c level_0/control_group.c level_0/control_group-common.c level_0/conversion.c level_0/conversion-common.c level_0/private-conversion.c level_0/directory.c level_0/private-directory.c level_0/environment.c level_0/private-environment.c level_0/file.c level_0/file-common.c level_0/private-file.c level_0/fss.c level_0/private-fss.c level_0/fss-common.c level_0/fss_named.c level_0/fss_nest.c level_0/fss_set.c level_0/iki.c level_0/iki-common.c level_0/private-iki.c level_0/limit.c level_0/memory.c level_0/memory_structure.c level_0/private-memory.c level_0/path.c level_0/path-common.c level_0/private-path.c level_0/pipe.c level_0/print.c level_0/print-common.c level_0/private-print.c level_0/serialize.c level_0/serialize-common.c level_0/private-serialize.c level_0/signal.c level_0/socket.c level_0/string.c level_0/string-common.c level_0/private-string.c level_0/string_dynamic.c level_0/string_map.c level_0/string_quantity.c level_0/string_range.c level_0/string_triple.c level_0/type_array.c level_0/private-type_array.c level_0/utf.c level_0/utf-common.c level_0/private-utf.c level_0/utf_dynamic.c level_0/utf_map.c level_0/utf_triple.c level_1/console.c level_1/control_group.c level_1/conversion.c level_1/private-conversion.c level_1/directory.c level_1/private-directory.c level_1/environment.c level_1/private-fss.c level_1/fss_basic.c level_1/fss_basic_list.c level_1/fss_embedded_list.c level_1/fss_extended.c level_1/fss_extended_list.c level_1/iki.c level_1/print.c level_1/private-print.c level_1/signal.c level_1/status.c level_1/string.c level_1/private-string.c level_1/utf.c level_1/private-utf.c level_1/utf_file.c level_1/private-utf_file.c level_2/control_group.c level_2/error.c level_2/error-common.c level_2/private-error.c level_2/execute.c level_2/private-execute.c level_2/file.c level_2/private-file.c level_2/fss.c level_2/private-fss.c level_2/fss_basic.c level_2/fss_basic_list.c level_2/fss_embedded_list.c level_2/fss_extended.c level_2/fss_extended_list.c level_2/fss_status.c level_2/iki.c level_2/private-iki.c level_2/path.c level_2/print.c level_2/program.c level_2/status.c +build_sources_library level_0/account.c level_0/private-account.c level_0/capability.c level_0/color.c level_0/color-common.c level_0/console.c level_0/console-common.c level_0/control_group.c level_0/control_group-common.c level_0/conversion.c level_0/conversion-common.c level_0/private-conversion.c level_0/directory.c level_0/private-directory.c level_0/environment.c level_0/private-environment.c level_0/execute.c level_0/file.c level_0/file-common.c level_0/private-file.c level_0/fss.c level_0/private-fss.c level_0/fss-common.c level_0/fss_named.c level_0/fss_nest.c level_0/fss_set.c level_0/iki.c level_0/iki-common.c level_0/private-iki.c level_0/limit.c level_0/memory.c level_0/memory_structure.c level_0/private-memory.c level_0/path.c level_0/path-common.c level_0/private-path.c level_0/pipe.c level_0/print.c level_0/print-common.c level_0/private-print.c level_0/serialize.c level_0/serialize-common.c level_0/private-serialize.c level_0/signal.c level_0/socket.c level_0/string.c level_0/string-common.c level_0/private-string.c level_0/string_dynamic.c level_0/string_map.c level_0/string_quantity.c level_0/string_range.c level_0/string_triple.c level_0/type_array.c level_0/private-type_array.c level_0/utf.c level_0/utf-common.c level_0/private-utf.c level_0/utf_dynamic.c level_0/utf_map.c level_0/utf_triple.c level_1/console.c level_1/control_group.c level_1/conversion.c level_1/private-conversion.c level_1/directory.c level_1/private-directory.c level_1/environment.c level_1/private-fss.c level_1/fss_basic.c level_1/fss_basic_list.c level_1/fss_embedded_list.c level_1/fss_extended.c level_1/fss_extended_list.c level_1/iki.c level_1/print.c level_1/private-print.c level_1/signal.c level_1/status.c level_1/string.c level_1/private-string.c level_1/utf.c level_1/private-utf.c level_1/utf_file.c level_1/private-utf_file.c level_2/control_group.c level_2/error.c level_2/error-common.c level_2/private-error.c level_2/execute.c level_2/private-execute.c level_2/file.c level_2/private-file.c level_2/fss.c level_2/private-fss.c level_2/fss_basic.c level_2/fss_basic_list.c level_2/fss_embedded_list.c level_2/fss_extended.c level_2/fss_extended_list.c level_2/fss_status.c level_2/iki.c level_2/private-iki.c level_2/path.c level_2/print.c level_2/program.c level_2/status.c build_sources_library-monolithic level_0/thread.c level_0/private-thread.c build_sources_program build_sources_headers level_0/account.h level_0/account-common.h level_0/capability.h level_0/capability-common.h level_0/color.h level_0/color-common.h level_0/console.h level_0/console-common.h level_0/control_group.h level_0/control_group-common.h level_0/conversion.h level_0/conversion-common.h level_0/directory.h level_0/directory_type.h level_0/directory-common.h level_0/environment.h level_0/environment-common.h level_0/execute.h level_0/execute-common.h level_0/file.h level_0/file-common.h level_0/fss.h level_0/fss-common.h level_0/fss_comment.h level_0/fss_delimit.h level_0/fss_named.h level_0/fss_nest.h level_0/fss_quote.h level_0/fss_set.h level_0/iki.h level_0/iki-common.h level_0/limit.h level_0/limit-common.h level_0/memory.h level_0/memory_structure.h level_0/memory-common.h level_0/path.h level_0/path-common.h level_0/pipe.h level_0/print.h level_0/print-common.h level_0/serialize.h level_0/serialize-common.h level_0/signal.h level_0/signal-common.h level_0/socket.h level_0/socket-common.h level_0/status.h level_0/string.h level_0/string-common.h level_0/string_dynamic.h level_0/string_map.h level_0/string_quantity.h level_0/string_range.h level_0/string_triple.h level_0/type.h level_0/type_array.h level_0/type_array-common.h level_0/utf.h level_0/utf-common.h level_0/utf_dynamic.h level_0/utf_map.h level_0/utf_triple.h level_1/console.h level_1/control_group.h level_1/conversion.h level_1/directory.h level_1/environment.h level_1/execute.h level_1/execute-common.h level_1/fss.h level_1/fss_basic.h level_1/fss_basic_list.h level_1/fss_embedded_list.h level_1/fss_extended.h level_1/fss_extended_list.h level_1/fss_status.h level_1/iki.h level_1/print.h level_1/signal.h level_1/signal-common.h level_1/status.h level_1/string.h level_1/utf.h level_1/utf_file.h level_2/control_group.h level_2/error.h level_2/error-common.h level_2/execute.h level_2/file.h level_2/fss.h level_2/fss_basic.h level_2/fss_basic_list.h level_2/fss_embedded_list.h level_2/fss_extended.h level_2/fss_extended_list.h level_2/fss_status.h level_2/iki.h level_2/path.h level_2/print.h level_2/program.h level_2/status.h diff --git a/level_0/f_execute/c/execute-common.h b/level_0/f_execute/c/execute-common.h index c1bb1f4..3dc01f3 100644 --- a/level_0/f_execute/c/execute-common.h +++ b/level_0/f_execute/c/execute-common.h @@ -17,6 +17,56 @@ extern "C" { #endif /** + * All special case execute codes. + * + * Only the lower 8-bits are available for return codes and so this is a special small set of status codes to be returned by a child process for a parent process to get. + * A waitpid() call can be used within the parent to get these codes if the child returns with these codes. + * + * All codes, except 0, represent an error. + * + * F_execute_code_last, this is intended to designate the last code provided by level_0 execute project. + * All code sets started by another project must start at this number + 1 with a code start map. + * + * Warning: F_execute and F_execute_not are F_status_t status codes and are not F_execute_codes. + * The F_execute_off is the execute code equivalent of F_execute_not. + */ +#ifndef _di_F_execute_codes_ + enum { + F_execute_none = 0, + F_execute_access, + F_execute_buffer, + F_execute_busy, + F_execute_capability, + F_execute_control_group, + F_execute_child, + F_execute_directory_not, + F_execute_failure, + F_execute_file_found_not, + F_execute_file_type_directory, + F_execute_group, + F_execute_input_output, + F_execute_limit, + F_execute_loop, + F_execute_memory_not, + F_execute_name_not, + F_execute_nice, + F_execute_off, + F_execute_parameter, + F_execute_pipe, + F_execute_processor, + F_execute_prohibited, + F_execute_resource_not, + F_execute_schedule, + F_execute_too_large, + F_execute_user, + F_execute_valid_not, + + // Required. + F_execute_code_last, + }; // enum +#endif // _di_F_execute_codes_ + +/** * A structure representing a scheduler and its parameters for execution. * * @todo move this into a f_scheduler project. @@ -39,6 +89,28 @@ extern "C" { scheduler.priority = 0; #endif // _di_f_execute_scheduler_t_ +/** + * A structure representing either a pid_t or an f_status_t. + * + * This is intended to be used for the execute functions that may store a pid_t in a parent process but a f_status_t in a child process. + * These would use the same variable, but it may be stored differently. + * + * status: The status code, generally used by the child process. + * pid: The process id (PID) of the child process, generally used by the parent process; + */ +#ifndef _di_f_execute_result_t_ + typedef union { + int status; + pid_t pid; + } f_execute_result_t; + + #define f_execute_result_t_initialize { 0 } + + #define f_execute_result_t_clear(execute_result) \ + execute_result.status = 0; \ + execute_result.pid = 0; +#endif // _di_f_execute_result_t_ + #ifdef __cplusplus } // extern "C" #endif diff --git a/level_0/f_execute/c/execute.c b/level_0/f_execute/c/execute.c new file mode 100644 index 0000000..050e940 --- /dev/null +++ b/level_0/f_execute/c/execute.c @@ -0,0 +1,247 @@ +#include "execute.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _di_f_execute_status_from_status_ + uint8_t f_execute_status_from_status(f_status_t status) { + + if (F_status_set_fine(status) == F_none) { + return F_execute_none; + } + + if (F_status_set_fine(status) == F_access) { + return F_execute_access; + } + + if (F_status_set_fine(status) == F_buffer) { + return F_execute_buffer; + } + + if (F_status_set_fine(status) == F_busy) { + return F_execute_busy; + } + + if (F_status_set_fine(status) == F_capability) { + return F_execute_capability; + } + + if (F_status_set_fine(status) == F_control_group) { + return F_execute_control_group; + } + + if (F_status_set_fine(status) == F_child) { + return F_execute_child; + } + + if (F_status_set_fine(status) == F_directory_not) { + return F_execute_directory_not; + } + + if (F_status_set_fine(status) == F_execute_not) { + return F_execute_off; + } + + if (F_status_set_fine(status) == F_failure) { + return F_execute_failure; + } + + if (F_status_set_fine(status) == F_file_found_not) { + return F_execute_file_found_not; + } + + if (F_status_set_fine(status) == F_file_type_directory) { + return F_execute_file_type_directory; + } + + if (F_status_set_fine(status) == F_group) { + return F_execute_group; + } + + if (F_status_set_fine(status) == F_input_output) { + return F_execute_input_output; + } + + if (F_status_set_fine(status) == F_limit) { + return F_execute_limit; + } + + if (F_status_set_fine(status) == F_loop) { + return F_execute_loop; + } + + if (F_status_set_fine(status) == F_memory_not) { + return F_execute_memory_not; + } + + if (F_status_set_fine(status) == F_name_not) { + return F_execute_name_not; + } + + if (F_status_set_fine(status) == F_nice) { + return F_execute_nice; + } + + if (F_status_set_fine(status) == F_parameter) { + return F_execute_parameter; + } + + if (F_status_set_fine(status) == F_pipe) { + return F_execute_pipe; + } + + if (F_status_set_fine(status) == F_processor) { + return F_execute_processor; + } + + if (F_status_set_fine(status) == F_prohibited) { + return F_execute_prohibited; + } + + if (F_status_set_fine(status) == F_resource_not) { + return F_execute_resource_not; + } + + if (F_status_set_fine(status) == F_schedule) { + return F_execute_schedule; + } + + if (F_status_set_fine(status) == F_too_large) { + return F_execute_too_large; + } + + if (F_status_set_fine(status) == F_user) { + return F_execute_user; + } + + if (F_status_set_fine(status) == F_valid_not) { + return F_execute_valid_not; + } + + return F_execute_code_last; + } +#endif // _di_f_execute_status_from_status_ + +#ifndef _di_f_execute_status_to_status_ + f_status_t f_execute_status_to_status(uint8_t status) { + + if (status == F_execute_none) { + return F_none; + } + + if (status == F_execute_access) { + return F_access; + } + + if (status == F_execute_buffer) { + return F_buffer; + } + + if (status == F_execute_busy) { + return F_busy; + } + + if (status == F_execute_capability) { + return F_capability; + } + + if (status == F_execute_control_group) { + return F_control_group; + } + + if (status == F_execute_child) { + return F_child; + } + + if (status == F_execute_directory_not) { + return F_directory_not; + } + + if (status == F_execute_failure) { + return F_failure; + } + + if (status == F_execute_file_found_not) { + return F_file_found_not; + } + + if (status == F_execute_file_type_directory) { + return F_file_type_directory; + } + + if (status == F_execute_group) { + return F_group; + } + + if (status == F_execute_input_output) { + return F_input_output; + } + + if (status == F_execute_limit) { + return F_limit; + } + + if (status == F_execute_loop) { + return F_loop; + } + + if (status == F_execute_memory_not) { + return F_memory_not; + } + + if (status == F_execute_name_not) { + return F_name_not; + } + + if (status == F_execute_nice) { + return F_nice; + } + + if (status == F_execute_off) { + return F_execute_not; + } + + if (status == F_execute_parameter) { + return F_parameter; + } + + if (status == F_execute_pipe) { + return F_pipe; + } + + if (status == F_execute_processor) { + return F_processor; + } + + if (status == F_execute_prohibited) { + return F_prohibited; + } + + if (status == F_execute_resource_not) { + return F_resource_not; + } + + if (status == F_execute_schedule) { + return F_schedule; + } + + if (status == F_execute_too_large) { + return F_too_large; + } + + if (status == F_execute_user) { + return F_user; + } + + if (status == F_execute_valid_not) { + return F_valid_not; + } + + return F_status_set_error(F_known_not); + } +#endif // _di_f_execute_status_to_status_ + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/level_0/f_execute/c/execute.h b/level_0/f_execute/c/execute.h index 7b87119..0b72146 100644 --- a/level_0/f_execute/c/execute.h +++ b/level_0/f_execute/c/execute.h @@ -27,6 +27,38 @@ extern "C" { #endif +/** + * Convert from F_status_t to execute status. + * + * @param status + * The status to convert from. + * The execute status does not support flags and so flags on this are ignored. + * + * @return + * The appropriate execute status code is returned on match. + * + * F_execute_code_last is returned when there are no matching execute states. + */ +#ifndef _di_f_execute_status_from_status_ + extern uint8_t f_execute_status_from_status(f_status_t status); +#endif // _di_f_execute_status_from_status_ + +/** + * Convert execute status to F_status_t. + * + * @param status + * An execute status number to convert. + * The execute status has no flags and is only 8-bits wide. + * + * @return + * The appropriate status code is returned on match (no bits, error or otherwise are set). + * + * F_known_not (with error bit) if the given number has no known execute state to convert. + */ +#ifndef _di_f_execute_status_to_status_ + extern f_status_t f_execute_status_to_status(uint8_t status); +#endif // _di_f_execute_status_to_status_ + #ifdef __cplusplus } // extern "C" #endif diff --git a/level_0/f_execute/data/build/settings b/level_0/f_execute/data/build/settings index 6320b83..91e42ab 100644 --- a/level_0/f_execute/data/build/settings +++ b/level_0/f_execute/data/build/settings @@ -21,7 +21,7 @@ build_indexer ar build_language c build_libraries -lc build_libraries-individual -lf_memory -lf_string -build_sources_library +build_sources_library execute.c build_sources_program build_sources_headers execute.h execute-common.h build_sources_script diff --git a/level_0/f_status/c/status.h b/level_0/f_status/c/status.h index 1ed6585..ad51a53 100644 --- a/level_0/f_status/c/status.h +++ b/level_0/f_status/c/status.h @@ -305,6 +305,8 @@ extern "C" { F_thread_not, F_time, F_time_not, + F_too_large, + F_too_small, F_user, F_user_not, F_utf, @@ -548,7 +550,7 @@ extern "C" { #endif // _di_F_status_access_ // Required. - F_status_code_last + F_status_code_last, }; // enum #endif // _di_F_status_codes_ diff --git a/level_1/fl_execute/data/build/settings b/level_1/fl_execute/data/build/settings index 909a461..16a2498 100644 --- a/level_1/fl_execute/data/build/settings +++ b/level_1/fl_execute/data/build/settings @@ -20,7 +20,7 @@ build_compiler gcc build_indexer ar build_language c build_libraries -lc -lcap -build_libraries-individual -lf_capability -lf_limit -lf_memory -lf_signal -lf_string -lf_type_array +build_libraries-individual -lf_capability -lf_execute -lf_limit -lf_memory -lf_signal -lf_string -lf_type_array build_sources_library build_sources_program build_sources_headers execute.h execute-common.h diff --git a/level_1/fl_status/c/status.c b/level_1/fl_status/c/status.c index dd97bdc..eb53745 100644 --- a/level_1/fl_status/c/status.c +++ b/level_1/fl_status/c/status.c @@ -681,6 +681,12 @@ extern "C" { case F_time_not: *string = FL_status_string_time_not; break; + case F_too_large: + *string = FL_status_string_too_large; + break; + case F_too_small: + *string = FL_status_string_too_small; + break; case F_known: *string = FL_status_string_known; break; diff --git a/level_1/fl_status/c/status.h b/level_1/fl_status/c/status.h index 6823fcf..293d9b5 100644 --- a/level_1/fl_status/c/status.h +++ b/level_1/fl_status/c/status.h @@ -321,6 +321,8 @@ extern "C" { #define FL_status_string_thread_not "F_thread_not" #define FL_status_string_time "F_time" #define FL_status_string_time_not "F_time_not" + #define FL_status_string_too_large "F_too_large" + #define FL_status_string_too_small "F_too_small" #define FL_status_string_success "F_success" #define FL_status_string_success_not "F_success_not" #define FL_status_string_supported "F_supported" @@ -504,6 +506,8 @@ extern "C" { #define FL_status_string_thread_not_length 12 #define FL_status_string_time_length 6 #define FL_status_string_time_not_length 10 + #define FL_status_string_too_large_length 11 + #define FL_status_string_too_small_length 11 #define FL_status_string_user_length 6 #define FL_status_string_user_not_length 10 #define FL_status_string_utf_length 5 diff --git a/level_2/fll_execute/c/execute.c b/level_2/fll_execute/c/execute.c index cea1d16..c4ea744 100644 --- a/level_2/fll_execute/c/execute.c +++ b/level_2/fll_execute/c/execute.c @@ -271,8 +271,35 @@ extern "C" { } // generally this does not return, but in some cases (such as with scripts) this does return so handle the results. + if (code < 0) { + if (errno == EACCES) code = F_execute_access; + else if (errno == E2BIG) code = F_execute_too_large; + else if (errno == EAGAIN) code = F_execute_resource_not; + else if (errno == EFAULT) code = F_execute_buffer; + else if (errno == EINVAL) code = F_execute_parameter; + else if (errno == EIO) code = F_execute_input_output; + else if (errno == EISDIR) code = F_execute_file_type_directory; + else if (errno == EIO) code = F_execute_input_output; + else if (errno == ELIBBAD) code = F_execute_valid_not; + else if (errno == ELOOP) code = F_execute_loop; + else if (errno == EMFILE) code = F_execute_resource_not; + else if (errno == ENAMETOOLONG) code = F_execute_name_not; + else if (errno == ENFILE) code = F_execute_resource_not; + else if (errno == ENOENT) code = F_execute_file_found_not; + else if (errno == ENOEXEC) code = F_execute_off; + else if (errno == ENOMEM) code = F_execute_memory_not; + else if (errno == ENOTDIR) code = F_execute_directory_not; + else if (errno == EPERM) code = F_execute_prohibited; + else if (errno == ETXTBSY) code = F_execute_busy; + else code = F_execute_failure; + } + else { + code = 0; + } + if (result) { - *result = code; + int *r = (int *) result; + *r = code; } if (option & fl_execute_parameter_option_exit) { diff --git a/level_2/fll_execute/c/execute.h b/level_2/fll_execute/c/execute.h index 8e2a43e..f177426 100644 --- a/level_2/fll_execute/c/execute.h +++ b/level_2/fll_execute/c/execute.h @@ -433,7 +433,7 @@ extern "C" { * @param as * (optional) This and most of its fields are optional and are disabled when set to NULL. * @param result - * (optional) The code returned after finishing execution of program. + * (optional) The execute status code returned after finishing or attempting to finish execution of program. * When fl_execute_parameter_option_return is passed via parameter.option, then this instead stores the child process id (PID). * This is should be of (int *) except when fl_execute_parameter_option_return this should instead be (pid_t *). * Set to NULL to not use. diff --git a/level_2/fll_execute/c/private-execute.c b/level_2/fll_execute/c/private-execute.c index 1ca0ce7..0d7562c 100644 --- a/level_2/fll_execute/c/private-execute.c +++ b/level_2/fll_execute/c/private-execute.c @@ -122,12 +122,11 @@ extern "C" { errno = 0; if (nice(*as.nice) == -1 && errno == -1) { - *result = -1; - if (parameter && parameter->option & fl_execute_parameter_option_exit) { - exit(*result); + exit(F_execute_nice); } + *result = F_execute_nice; return F_status_set_error(F_nice); } } @@ -136,48 +135,45 @@ extern "C" { const f_status_t status = f_capability_process_set(as.capability); if (F_status_is_error(status) && F_status_set_fine(status) != F_supported_not) { - *result = -1; - if (parameter && parameter->option & fl_execute_parameter_option_exit) { - exit(*result); + exit(F_execute_capability); } + *result = F_execute_capability; return F_status_set_error(F_capability); } } if (as.id_groups) { if (setgroups(as.id_groups->used, (const gid_t *) as.id_groups->array) == -1) { - *result = -1; if (parameter && parameter->option & fl_execute_parameter_option_exit) { - exit(*result); + exit(F_execute_group); } + *result = F_execute_group; return F_status_set_error(F_group); } } if (as.id_group) { if (setgid(*as.id_group) == -1) { - *result = -1; - if (parameter && parameter->option & fl_execute_parameter_option_exit) { - exit(*result); + exit(F_execute_group); } + *result = F_execute_group; return F_status_set_error(F_group); } } if (as.id_user) { if (setuid(*as.id_user) == -1) { - *result = -1; - if (parameter && parameter->option & fl_execute_parameter_option_exit) { - exit(*result); + exit(F_execute_user); } + *result = F_execute_user; return F_status_set_error(F_user); } } @@ -355,11 +351,11 @@ extern "C" { if (result) { int *r = (int *) result; - *r = -1; + *r = F_execute_failure; } if (parameter && parameter->option & fl_execute_parameter_option_exit) { - exit(-1); + exit(F_execute_failure); } return F_child; @@ -403,7 +399,33 @@ extern "C" { if (F_status_is_error(status)) return status; } - const int code = direct ? execv(program, fixed_arguments) : execvp(program, fixed_arguments); + int code = direct ? execv(program, fixed_arguments) : execvp(program, fixed_arguments); + + if (code < 0) { + if (errno == EACCES) code = F_execute_access; + else if (errno == E2BIG) code = F_execute_too_large; + else if (errno == EAGAIN) code = F_execute_resource_not; + else if (errno == EFAULT) code = F_execute_buffer; + else if (errno == EINVAL) code = F_execute_parameter; + else if (errno == EIO) code = F_execute_input_output; + else if (errno == EISDIR) code = F_execute_file_type_directory; + else if (errno == EIO) code = F_execute_input_output; + else if (errno == ELIBBAD) code = F_execute_valid_not; + else if (errno == ELOOP) code = F_execute_loop; + else if (errno == EMFILE) code = F_execute_resource_not; + else if (errno == ENAMETOOLONG) code = F_execute_name_not; + else if (errno == ENFILE) code = F_execute_resource_not; + else if (errno == ENOENT) code = F_execute_file_found_not; + else if (errno == ENOEXEC) code = F_execute_off; + else if (errno == ENOMEM) code = F_execute_memory_not; + else if (errno == ENOTDIR) code = F_execute_directory_not; + else if (errno == EPERM) code = F_execute_prohibited; + else if (errno == ETXTBSY) code = F_execute_busy; + else code = F_execute_failure; + } + else { + code = 0; + } if (result) { int *r = (int *) result; @@ -523,8 +545,8 @@ extern "C" { close(descriptors[0]); if (result) { - int *r = (int *) result; - *r = -1; + f_status_t *r = (f_status_t *) result; + *r = F_status_set_error(F_failure); } if (parameter && parameter->option & fl_execute_parameter_option_exit) { @@ -570,11 +592,42 @@ extern "C" { if (F_status_is_error(status)) return status; } - const int code = direct ? execv(program, fixed_arguments) : execvp(program, fixed_arguments); + int code = direct ? execv(program, fixed_arguments) : execvp(program, fixed_arguments); // close the write pipe for the child when done. close(descriptors[0]); + if (code < 0) { + if (errno == EACCES) code = F_execute_access; + else if (errno == E2BIG) code = F_execute_too_large; + else if (errno == EAGAIN) code = F_execute_resource_not; + else if (errno == EFAULT) code = F_execute_buffer; + else if (errno == EINVAL) code = F_execute_parameter; + else if (errno == EIO) code = F_execute_input_output; + else if (errno == EISDIR) code = F_execute_file_type_directory; + else if (errno == EIO) code = F_execute_input_output; + else if (errno == ELIBBAD) code = F_execute_valid_not; + else if (errno == ELOOP) code = F_execute_loop; + else if (errno == EMFILE) code = F_execute_resource_not; + else if (errno == ENAMETOOLONG) code = F_execute_name_not; + else if (errno == ENFILE) code = F_execute_resource_not; + else if (errno == ENOENT) code = F_execute_file_found_not; + else if (errno == ENOEXEC) code = F_execute_off; + else if (errno == ENOMEM) code = F_execute_memory_not; + else if (errno == ENOTDIR) code = F_execute_directory_not; + else if (errno == EPERM) code = F_execute_prohibited; + else if (errno == ETXTBSY) code = F_execute_busy; + else code = F_execute_failure; + } + else { + code = 0; + } + + if (result) { + int *r = (int *) result; + *r = code; + } + if (result) { int *r = (int *) result; *r = code; diff --git a/level_2/fll_execute/c/private-execute.h b/level_2/fll_execute/c/private-execute.h index 8e79301..f0bf921 100644 --- a/level_2/fll_execute/c/private-execute.h +++ b/level_2/fll_execute/c/private-execute.h @@ -203,7 +203,7 @@ extern "C" { * @param as * (optional) This and most of its fields are optional and are disabled when set to NULL. * @param result - * (optional) The code returned after finishing execution of program. + * (optional) The execute status code returned after finishing or attempting to finish execution of program. * When fl_execute_parameter_option_return is passed via parameter.option, then this instead stores the child process id (PID). * This is should be of (int *) except when fl_execute_parameter_option_return this should instead be (pid_t *). * Set to NULL to not use. @@ -279,7 +279,7 @@ extern "C" { * @param as * (optional) This and most of its fields are optional and are disabled when set to NULL. * @param result - * (optional) The code returned after finishing execution of program. + * (optional) The execute status code returned after finishing or attempting to finish execution of program. * When fl_execute_parameter_option_return is passed via parameter.option, then this instead stores the child process id (PID). * This is should be of (int *) except when fl_execute_parameter_option_return this should instead be (pid_t *). * Set to NULL to not use. diff --git a/level_2/fll_execute/data/build/settings b/level_2/fll_execute/data/build/settings index 18994db..d895a10 100644 --- a/level_2/fll_execute/data/build/settings +++ b/level_2/fll_execute/data/build/settings @@ -20,8 +20,8 @@ build_compiler gcc build_indexer ar build_language c build_libraries -lc -lcap -build_libraries-individual -lfl_control_group -lfl_environment -lf_account -lf_capability -lf_control_group -lf_environment -lf_file -lf_limit -lf_memory -lf_path -lf_signal -lf_string -lf_thread -lf_type_array -lf_utf -build_libraries-individual_threadless -lfl_control_group -lfl_environment -lf_account -lf_capability -lf_control_group -lf_environment -lf_file -lf_limit -lf_memory -lf_path -lf_signal -lf_string -lf_type_array -lf_utf +build_libraries-individual -lfl_control_group -lfl_environment -lf_account -lf_capability -lf_control_group -lf_environment -lf_execute -lf_file -lf_limit -lf_memory -lf_path -lf_signal -lf_string -lf_thread -lf_type_array -lf_utf +build_libraries-individual_threadless -lfl_control_group -lfl_environment -lf_account -lf_capability -lf_control_group -lf_environment -lf_execute -lf_file -lf_limit -lf_memory -lf_path -lf_signal -lf_string -lf_type_array -lf_utf build_sources_library execute.c private-execute.c build_sources_program build_sources_headers execute.h diff --git a/level_2/fll_status/c/status.c b/level_2/fll_status/c/status.c index 0a1e1b5..cc2f46f 100644 --- a/level_2/fll_status/c/status.c +++ b/level_2/fll_status/c/status.c @@ -1393,6 +1393,18 @@ extern "C" { return F_none; } + if (fl_string_compare(string, FL_status_string_too_large, length, FL_status_string_too_large_length) == F_equal_to) { + *code = F_too_large; + + return F_none; + } + + if (fl_string_compare(string, FL_status_string_too_small, length, FL_status_string_too_small_length) == F_equal_to) { + *code = F_too_small; + + return F_none; + } + if (fl_string_compare(string, FL_status_string_user, length, FL_status_string_user_length) == F_equal_to) { *code = F_user; diff --git a/level_3/controller/c/private-common.c b/level_3/controller/c/private-common.c index bd6e142..fe1c530 100644 --- a/level_3/controller/c/private-common.c +++ b/level_3/controller/c/private-common.c @@ -919,6 +919,17 @@ extern "C" { } #endif // _di_controller_time_ +#ifndef _di_controller_time_micro_ + struct timespec controller_time_micro(const f_number_unsigned_t microseconds) { + + struct timespec time; + time.tv_sec = microseconds / 1000; + time.tv_nsec = (time.tv_sec ? microseconds - time.tv_sec : microseconds) * 1000; + + return time; + } +#endif // _di_controller_time_micro_ + #ifdef __cplusplus } // extern "C" #endif diff --git a/level_3/controller/c/private-common.h b/level_3/controller/c/private-common.h index 7855177..f727163 100644 --- a/level_3/controller/c/private-common.h +++ b/level_3/controller/c/private-common.h @@ -1077,6 +1077,7 @@ extern "C" { * - control: The process is started from a control operation. * * id: The ID of this process relative to the processes array. + * result: The last return code from an execution of a process. * status: The last execution status of the process. * state: The state of the process. * action: The action being performed. @@ -1122,6 +1123,8 @@ extern "C" { uint8_t options; uint8_t type; + int result; + f_thread_id_t id_thread; f_thread_lock_t lock; f_thread_lock_t active; @@ -1147,6 +1150,7 @@ extern "C" { 0, \ 0, \ 0, \ + 0, \ f_thread_id_t_initialize, \ f_thread_lock_t_initialize, \ f_thread_lock_t_initialize, \ @@ -2437,6 +2441,19 @@ extern "C" { void controller_time(const time_t seconds, const long nanoseconds, struct timespec *time) f_attribute_visibility_internal; #endif // _di_controller_time_ +/** + * Convert microseconds to nanoseconds. + * + * @param microseconds + * The number of microseconds. + * + * @return + * A time structure suitable for passing to nanosleep() and similar functions. + */ +#ifndef _di_controller_time_micro_ + extern struct timespec controller_time_micro(const f_number_unsigned_t microseconds) f_attribute_visibility_internal; +#endif // _di_controller_time_micro_ + #ifdef __cplusplus } // extern "C" #endif diff --git a/level_3/controller/c/private-controller.c b/level_3/controller/c/private-controller.c index 24a8ab6..a84419d 100644 --- a/level_3/controller/c/private-controller.c +++ b/level_3/controller/c/private-controller.c @@ -1195,7 +1195,7 @@ extern "C" { int result = 0; - status = fll_execute_into(0, entry_action->parameters, fl_execute_parameter_option_path, 0, &result); + status = fll_execute_into(0, entry_action->parameters, fl_execute_parameter_option_path, 0, (void *) &result); if (F_status_is_error(status)) { if (F_status_set_fine(status) == F_file_found_not) { diff --git a/level_3/controller/c/private-rule.c b/level_3/controller/c/private-rule.c index e55ec4c..fa16b8e 100644 --- a/level_3/controller/c/private-rule.c +++ b/level_3/controller/c/private-rule.c @@ -129,6 +129,49 @@ extern "C" { } #endif // _di_controller_rule_parameters_read_ +#ifndef _di_controller_rule_action_type_to_action_execute_type_ + uint8_t controller_rule_action_type_to_action_execute_type(const uint8_t type) { + + if (type == controller_rule_action_type_freeze) { + return controller_rule_action_type_execute_freeze; + } + + if (type == controller_rule_action_type_kill) { + return controller_rule_action_type_execute_kill; + } + + if (type == controller_rule_action_type_pause) { + return controller_rule_action_type_execute_pause; + } + + if (type == controller_rule_action_type_reload) { + return controller_rule_action_type_execute_reload; + } + + if (type == controller_rule_action_type_restart) { + return controller_rule_action_type_execute_restart; + } + + if (type == controller_rule_action_type_resume) { + return controller_rule_action_type_execute_resume; + } + + if (type == controller_rule_action_type_start) { + return controller_rule_action_type_execute_start; + } + + if (type == controller_rule_action_type_stop) { + return controller_rule_action_type_execute_stop; + } + + if (type == controller_rule_action_type_thaw) { + return controller_rule_action_type_execute_thaw; + } + + return controller_rule_action_type_execute__enum_size; + } +#endif // _di_controller_rule_action_type_to_action_execute_type_ + #ifndef _di_controller_rule_action_type_name_ f_string_static_t controller_rule_action_type_name(const uint8_t type) { @@ -212,6 +255,64 @@ extern "C" { } #endif // _di_controller_rule_action_type_name_ +#ifndef _di_controller_rule_action_type_execute_name_ + f_string_static_t controller_rule_action_type_execute_name(const uint8_t type) { + + f_string_static_t buffer = f_string_static_t_initialize; + + switch (type) { + case controller_rule_action_type_execute_freeze: + buffer.string = controller_string_freeze_s; + buffer.used = controller_string_freeze_length; + break; + + case controller_rule_action_type_execute_kill: + buffer.string = controller_string_kill_s; + buffer.used = controller_string_kill_length; + break; + + case controller_rule_action_type_execute_pause: + buffer.string = controller_string_pause_s; + buffer.used = controller_string_pause_length; + break; + + case controller_rule_action_type_execute_reload: + buffer.string = controller_string_reload_s; + buffer.used = controller_string_reload_length; + break; + + case controller_rule_action_type_execute_restart: + buffer.string = controller_string_restart_s; + buffer.used = controller_string_restart_length; + break; + + case controller_rule_action_type_execute_resume: + buffer.string = controller_string_resume_s; + buffer.used = controller_string_resume_length; + break; + + case controller_rule_action_type_execute_start: + buffer.string = controller_string_start_s; + buffer.used = controller_string_start_length; + break; + + case controller_rule_action_type_execute_stop: + buffer.string = controller_string_stop_s; + buffer.used = controller_string_stop_length; + break; + + case controller_rule_action_type_execute_thaw: + buffer.string = controller_string_thaw_s; + buffer.used = controller_string_thaw_length; + break; + } + + buffer.size = buffer.used; + + return buffer; + } +#endif // _di_controller_rule_action_type_execute_name_ + #ifndef _di_controller_rule_actions_increase_by_ f_status_t controller_rule_actions_increase_by(const f_array_length_t amount, controller_rule_actions_t *actions) { @@ -1015,9 +1116,15 @@ extern "C" { fl_print_format("%['.%]%c", print.to.stream, print.context, print.context, f_string_eol_s[0]); } else if (code) { - fl_print_format("%[' failed with the exit code %]", print.to.stream, print.context, print.context); - fl_print_format("%[%i%]", print.to.stream, print.notable, code, print.notable); - fl_print_format("%[.%]%c", print.to.stream, print.context, print.context, f_string_eol_s[0]); + if (code == F_execute_file_found_not) { + fl_print_format("%[' could not be executed because it was not found.%]%c", print.to.stream, print.context, print.context, f_string_eol_s[0]); + } + else { + // @todo improve reporting of all known status codes. + fl_print_format("%[' failed with the exit code %]", print.to.stream, print.context, print.context); + fl_print_format("%[%i%]", print.to.stream, print.notable, code, print.notable); + fl_print_format("%[.%]%c", print.to.stream, print.context, print.context, f_string_eol_s[0]); + } } else { fl_print_format("%[' failed.%]%c", print.to.stream, print.context, print.context, f_string_eol_s[0]); @@ -1028,17 +1135,6 @@ extern "C" { } #endif // _di_controller_rule_item_error_print_execute_ -#ifndef _di_controller_rule_item_error_print_execute_not_found_ - void controller_rule_item_error_print_execute_not_found(const fll_error_print_t print, const bool script_is, const f_string_t name) { - - if (print.verbosity == f_console_verbosity_quiet) return; - - fl_print_format("%c%[%SThe %s '%]", print.to.stream, f_string_eol_s[0], print.context, print.prefix, script_is ? controller_string_script_s : controller_string_program_s, print.context); - fl_print_format("%[%S%]", print.to.stream, print.notable, name, print.notable); - fl_print_format("%[' could not be executed because it was not found.%]%c", print.to.stream, print.context, print.context, f_string_eol_s[0]); - } -#endif // _di_controller_rule_item_error_print_execute_not_found_ - #ifndef _di_controller_rule_item_error_print_need_want_wish_ void controller_rule_item_error_print_need_want_wish(const fll_error_print_t print, const f_string_t need_want_wish, const f_string_t value, const f_string_t why) { @@ -1169,9 +1265,20 @@ extern "C" { execute_set.parameter.option |= fl_execute_parameter_option_path; } - // @todo: wrap these executions (foreground and background) in an additional loop to re-execution on the given re-run conditions. if (process->rule.items.array[i].type == controller_rule_item_type_command) { - status = controller_rule_execute_foreground(process->rule.items.array[i].type, process->rule.items.array[i].actions.array[j], 0, process->rule.items.array[i].actions.array[j].parameters, options, global, &execute_set, process); + for (;;) { + + status = controller_rule_execute_foreground(process->rule.items.array[i].type, process->rule.items.array[i].actions.array[j], 0, process->rule.items.array[i].actions.array[j].parameters, options, global, &execute_set, process); + + if (status == F_child || status == F_signal || F_status_set_fine(status) == F_lock) break; + if (F_status_is_error(status) && F_status_set_fine(status) != F_failure) break; + + if (controller_rule_execute_rerun(global, controller_rule_action_type_to_action_execute_type(action), process, &process->rule.items.array[i]) > 0) { + continue; + } + + break; + } // for if (status == F_child || status == F_signal || F_status_set_fine(status) == F_lock) break; @@ -1189,7 +1296,19 @@ extern "C" { else if (process->rule.items.array[i].type == controller_rule_item_type_script) { execute_set.parameter.data = &process->rule.items.array[i].actions.array[j].parameters.array[0]; - status = controller_rule_execute_foreground(process->rule.items.array[i].type, process->rule.items.array[i].actions.array[j], process->rule.script.used ? process->rule.script.string : controller_default_program_script, arguments_none, options, global, &execute_set, process); + for (;;) { + + status = controller_rule_execute_foreground(process->rule.items.array[i].type, process->rule.items.array[i].actions.array[j], process->rule.script.used ? process->rule.script.string : controller_default_program_script, arguments_none, options, global, &execute_set, process); + + if (status == F_child || status == F_signal || F_status_set_fine(status) == F_lock) break; + if (F_status_is_error(status) && F_status_set_fine(status) != F_failure) break; + + if (controller_rule_execute_rerun(global, controller_rule_action_type_to_action_execute_type(action), process, &process->rule.items.array[i]) > 0) { + continue; + } + + break; + } // for if (status == F_child || status == F_signal || F_status_set_fine(status) == F_lock) break; @@ -1206,7 +1325,19 @@ extern "C" { } else if (process->rule.items.array[i].type == controller_rule_item_type_service) { if (process->rule.items.array[i].pid_file.used) { - status = controller_rule_execute_pid_with(process->rule.items.array[i].pid_file, process->rule.items.array[i].type, process->rule.items.array[i].actions.array[j], 0, process->rule.items.array[i].actions.array[j].parameters, options, process->rule.items.array[i].with, global, &execute_set, process); + for (;;) { + + status = controller_rule_execute_pid_with(process->rule.items.array[i].pid_file, process->rule.items.array[i].type, process->rule.items.array[i].actions.array[j], 0, process->rule.items.array[i].actions.array[j].parameters, options, process->rule.items.array[i].with, global, &execute_set, process); + + if (status == F_child || status == F_signal || F_status_set_fine(status) == F_lock) break; + if (F_status_is_error(status) && F_status_set_fine(status) != F_failure) break; + + if (controller_rule_execute_rerun(global, controller_rule_action_type_to_action_execute_type(action), process, &process->rule.items.array[i]) > 0) { + continue; + } + + break; + } // for if (status == F_child || status == F_signal || F_status_set_fine(status) == F_lock) break; @@ -1232,7 +1363,19 @@ extern "C" { if (process->rule.items.array[i].pid_file.used) { execute_set.parameter.data = &process->rule.items.array[i].actions.array[j].parameters.array[0]; - status = controller_rule_execute_pid_with(process->rule.items.array[i].pid_file, process->rule.items.array[i].type, process->rule.items.array[i].actions.array[j], process->rule.script.used ? process->rule.script.string : controller_default_program_script, arguments_none, options, process->rule.items.array[i].with, global, &execute_set, process); + for (;;) { + + status = controller_rule_execute_pid_with(process->rule.items.array[i].pid_file, process->rule.items.array[i].type, process->rule.items.array[i].actions.array[j], process->rule.script.used ? process->rule.script.string : controller_default_program_script, arguments_none, options, process->rule.items.array[i].with, global, &execute_set, process); + + if (status == F_child || status == F_signal || F_status_set_fine(status) == F_lock) break; + if (F_status_is_error(status) && F_status_set_fine(status) != F_failure) break; + + if (controller_rule_execute_rerun(global, controller_rule_action_type_to_action_execute_type(action), process, &process->rule.items.array[i]) > 0) { + continue; + } + + break; + } // for if (status == F_child || status == F_signal || F_status_set_fine(status) == F_lock) break; @@ -1317,8 +1460,7 @@ extern "C" { f_status_t status = F_none; f_status_t status_lock = F_none; - int result = 0; - pid_t id_child = 0; + f_execute_result_t result = f_execute_result_t_initialize; status = controller_pids_increase(&process->childs); @@ -1376,20 +1518,24 @@ extern "C" { } // sleep for less than a second to better show simulation of synchronous vs asynchronous. - usleep(controller_thread_simulation_timeout); + { + struct timespec delay = controller_time_micro(controller_thread_simulation_timeout); + nanosleep(&delay, 0); + } const f_string_static_t simulated_program = macro_f_string_static_t_initialize(f_string_empty_s, 0); const f_string_statics_t simulated_arguments = f_string_statics_t_initialize; fl_execute_parameter_t simulated_parameter = macro_fl_execute_parameter_t_initialize(execute_set->parameter.option, execute_set->parameter.wait, process->rule.has & controller_rule_has_environment ? execute_set->parameter.environment : 0, execute_set->parameter.signals, &simulated_program); - status = fll_execute_program(controller_default_program_script, simulated_arguments, &simulated_parameter, &execute_set->as, simulated_parameter.option & fl_execute_parameter_option_return ? (void *) &id_child : (void *) &result); + status = fll_execute_program(controller_default_program_script, simulated_arguments, &simulated_parameter, &execute_set->as, (void *) &result); } else { - status = fll_execute_program(program, arguments, &execute_set->parameter, &execute_set->as, execute_set->parameter.option & fl_execute_parameter_option_return ? (void *) &id_child : (void *) &result); + status = fll_execute_program(program, arguments, &execute_set->parameter, &execute_set->as, (void *) &result); } if (status == F_parent) { - result = 0; + const pid_t id_child = result.pid; + result.status = 0; f_thread_unlock(&process->lock); @@ -1423,7 +1569,7 @@ extern "C" { if (status_lock != F_signal) { // have the parent wait for the child process to finish. - waitpid(id_child, &result, 0); + waitpid(id_child, &result.status, 0); } if (status_lock == F_signal || !controller_thread_is_enabled_process(process, global.thread)) { @@ -1454,6 +1600,8 @@ extern "C" { return F_status_set_error(F_lock); } + process->result = result.status; + // remove the pid now that waidpid() has returned. *child = 0; @@ -1467,8 +1615,7 @@ extern "C" { return F_status_set_error(F_lock); } - // this must explicitly check for 0 (as opposed to checking (!result)). - if (!WIFEXITED(result)) { + if (WIFEXITED(result.status) ? WEXITSTATUS(result.status) : 0) { status = F_status_set_error(F_failure); } else { @@ -1476,6 +1623,8 @@ extern "C" { } } else { + global.main->child = result.status; + if (!controller_thread_is_enabled_process(process, global.thread)) { return F_signal; } @@ -1496,22 +1645,11 @@ extern "C" { return status; } - if (result != 0) { - status = F_status_set_error(F_failure); - } - if (F_status_is_error(status)) { status = F_status_set_fine(status); - if (status == F_control_group || status == F_failure || status == F_limit || status == F_processor || status == F_schedule) { - controller_rule_item_error_print_execute(global.main->error, type == controller_rule_item_type_script, program ? program : arguments.used ? arguments.array[0].string : f_string_empty_s, result, status, global.thread); - } - else if (status == F_file_found_not) { - controller_print_lock(global.main->error.to, global.thread); - - controller_rule_item_error_print_execute_not_found(global.main->error, F_false, program); - - controller_print_unlock_flush(global.main->error.to, global.thread); + if ((WIFEXITED(process->result) && WEXITSTATUS(process->result)) || status == F_control_group || status == F_failure || status == F_limit || status == F_processor || status == F_schedule) { + controller_rule_item_error_print_execute(global.main->error, type == controller_rule_item_type_script, program ? program : arguments.used ? arguments.array[0].string : f_string_empty_s, WIFEXITED(process->result) ? WEXITSTATUS(process->result) : 0, status, global.thread); } else { controller_error_print(global.main->error, F_status_set_fine(status), "fll_execute_program", F_true, global.thread); @@ -1530,8 +1668,7 @@ extern "C" { f_status_t status = F_none; f_status_t status_lock = F_none; - int result = 0; - pid_t id_child = 0; + f_execute_result_t result = f_execute_result_t_initialize; status = controller_pids_increase(&process->childs); @@ -1629,20 +1766,24 @@ extern "C" { } // sleep for less than a second to better show simulation of synchronous vs asynchronous. - usleep(controller_thread_simulation_timeout); + { + struct timespec delay = controller_time_micro(controller_thread_simulation_timeout); + nanosleep(&delay, 0); + } const f_string_static_t simulated_program = macro_f_string_static_t_initialize(f_string_empty_s, 0); const f_string_statics_t simulated_arguments = f_string_statics_t_initialize; fl_execute_parameter_t simulated_parameter = macro_fl_execute_parameter_t_initialize(execute_set->parameter.option, execute_set->parameter.wait, process->rule.has & controller_rule_has_environment ? execute_set->parameter.environment : 0, execute_set->parameter.signals, &simulated_program); - status = fll_execute_program(controller_default_program_script, simulated_arguments, &simulated_parameter, &execute_set->as, simulated_parameter.option & fl_execute_parameter_option_return ? (void *) &id_child : (void *) &result); + status = fll_execute_program(controller_default_program_script, simulated_arguments, &simulated_parameter, &execute_set->as, (void *) &result); } else { - status = fll_execute_program(program, arguments, &execute_set->parameter, &execute_set->as, execute_set->parameter.option & fl_execute_parameter_option_return ? (void *) &id_child : (void *) &result); + status = fll_execute_program(program, arguments, &execute_set->parameter, &execute_set->as, (void *) &result); } if (status == F_parent) { - result = 0; + const pid_t id_child = result.pid; + result.status = 0; f_thread_unlock(&process->lock); @@ -1676,7 +1817,7 @@ extern "C" { 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); + waitpid(id_child, &result.status, 0); } if (!controller_thread_is_enabled_process(process, global.thread)) { @@ -1707,6 +1848,8 @@ extern "C" { return F_status_set_error(F_lock); } + process->result = result.status; + // remove the pid now that waidpid() has returned. *child = 0; @@ -1720,9 +1863,7 @@ extern "C" { return F_status_set_error(F_lock); } - // this must explicitly check for 0 (as opposed to checking (!result)). - // @todo expand this to provide user more control over what is or is not an error to designate as a failure. - if (!WIFEXITED(result)) { + if (WIFEXITED(result.status) ? WEXITSTATUS(result.status) : 0) { status = F_status_set_error(F_failure); } else { @@ -1730,6 +1871,8 @@ extern "C" { } } else { + global.main->child = result.status; + if (!controller_thread_is_enabled_process(process, global.thread)) { return F_signal; } @@ -1750,22 +1893,11 @@ extern "C" { return status; } - if (result != 0) { - status = F_status_set_error(F_failure); - } - if (F_status_is_error(status)) { status = F_status_set_fine(status); - if (status == F_control_group || status == F_failure || status == F_limit || status == F_processor || status == F_schedule) { - controller_rule_item_error_print_execute(global.main->error, type == controller_rule_item_type_utility, program ? program : arguments.used ? arguments.array[0].string : f_string_empty_s, result, status, global.thread); - } - else if (status == F_file_found_not) { - controller_print_lock(global.main->error.to, global.thread); - - controller_rule_item_error_print_execute_not_found(global.main->error, F_false, program); - - controller_print_unlock_flush(global.main->error.to, global.thread); + if ((WIFEXITED(process->result) && WEXITSTATUS(process->result)) || status == F_control_group || status == F_failure || status == F_limit || status == F_processor || status == F_schedule) { + controller_rule_item_error_print_execute(global.main->error, type == controller_rule_item_type_utility, program ? program : arguments.used ? arguments.array[0].string : f_string_empty_s, WIFEXITED(process->result) ? WEXITSTATUS(process->result) : 0, status, global.thread); } else { controller_error_print(global.main->error, F_status_set_fine(status), "fll_execute_program", F_true, global.thread); @@ -1778,6 +1910,75 @@ extern "C" { } #endif // _di_controller_rule_execute_pid_with_ +#ifndef _di_controller_rule_execute_rerun_ + int8_t controller_rule_execute_rerun(const controller_global_t global, const uint8_t action, controller_process_t *process, controller_rule_item_t *item) { + + const int result = WIFEXITED(process->result) ? WEXITSTATUS(process->result) : 0; + + if (item->reruns[action].is & (result ? controller_rule_rerun_is_failure : controller_rule_rerun_is_success)) { + controller_rule_rerun_item_t *rerun_item = result ? &item->reruns[action].failure : &item->reruns[action].success; + + if (!controller_thread_is_enabled_process(process, global.thread)) return -2; + + if (!rerun_item->max || rerun_item->count < rerun_item->max) { + if (global.main->error.verbosity == f_console_verbosity_debug) { + controller_print_lock(global.main->output, global.thread); + + fl_print_format("%cRe-running '", global.main->output.stream, f_string_eol_s[0]); + fl_print_format("%[%q%]", global.main->output.stream, global.main->context.set.title, process->rule.alias, global.main->context.set.title); + f_print_terminated("' '", global.main->output.stream); + fl_print_format("%[%q%]", global.main->output.stream, global.main->context.set.notable, controller_rule_action_type_execute_name(action), global.main->context.set.notable); + f_print_terminated("' with a ", global.main->output.stream); + fl_print_format("%[%s%]", global.main->output.stream, global.main->context.set.notable, controller_string_delay_s, global.main->context.set.notable); + f_print_terminated(" of ", global.main->output.stream); + fl_print_format("%[%ul%] MegaTime", global.main->output.stream, global.main->context.set.notable, rerun_item->delay, global.main->context.set.notable); + + if (rerun_item->max) { + f_print_terminated(" for ", global.main->output.stream); + fl_print_format("%[%ul%]", global.main->output.stream, global.main->context.set.notable, rerun_item->count, global.main->context.set.notable); + f_print_terminated(" of ", global.main->output.stream); + fl_print_format("%[%s%] ", global.main->output.stream, global.main->context.set.notable, controller_string_max_s, global.main->context.set.notable); + fl_print_format("%[%ul%]", global.main->output.stream, global.main->context.set.notable, rerun_item->max, global.main->context.set.notable); + fl_print_format(".%c", global.main->output.stream, f_string_eol_s[0]); + } + else { + fl_print_format(" with no %[%s%].%c", global.main->output.stream, global.main->context.set.notable, controller_string_max_s, global.main->context.set.notable, f_string_eol_s[0]); + } + + controller_print_unlock_flush(global.main->output, global.thread); + } + + if (rerun_item->delay) { + struct timespec time = controller_time_micro(rerun_item->delay); + + if (nanosleep(&time, 0) < 0) { + return -1; + } + + if (!controller_thread_is_enabled_process(process, global.thread)) return -2; + } + + if (item->reruns[action].is & (result ? controller_rule_rerun_is_failure_reset : controller_rule_rerun_is_success_reset)) { + if (result) { + item->reruns[action].success.count = 0; + } + else { + item->reruns[action].failure.count = 0; + } + } + + if (rerun_item->max) { + ++rerun_item->count; + } + + return F_true; + } + } + + return F_false; + } +#endif // _di_controller_rule_execute_rerun_ + #ifndef _di_controller_rule_id_construct_ f_status_t controller_rule_id_construct(const controller_global_t global, const f_string_static_t source, const f_string_range_t directory, const f_string_range_t basename, f_string_dynamic_t *alias) { @@ -2604,7 +2805,7 @@ extern "C" { if (process->rule.items.used) { fl_print_format("%c%[%SThe rule '%]", global.main->error.to.stream, f_string_eol_s[0], global.main->error.context, global.main->error.prefix, global.main->error.context); fl_print_format("%[%Q%]", global.main->error.to.stream, global.main->error.notable, process->rule.name, global.main->error.notable); - fl_print_format("%[' has no '%]", global.main->error.to.stream, global.main->error.context, process->rule.name, global.main->error.context); + fl_print_format("%[' has no '%]", global.main->error.to.stream, global.main->error.context, global.main->error.context); fl_print_format("%[%q%]", global.main->error.to.stream, global.main->error.notable, controller_rule_action_type_name(process->action), global.main->error.notable); fl_print_format("%[' action to execute.%]%c", global.main->error.to.stream, global.main->error.context, global.main->error.context, f_string_eol_s[0]); } diff --git a/level_3/controller/c/private-rule.h b/level_3/controller/c/private-rule.h index 64a0f76..45594e3 100644 --- a/level_3/controller/c/private-rule.h +++ b/level_3/controller/c/private-rule.h @@ -80,6 +80,22 @@ extern "C" { #endif // _di_controller_rule_parameters_read_ /** + * Convert the action type to an action execute type. + * + * @param type + * The action type to convert from. + * + * @return + * The converted action type, converted into an action execute type. + * + * The code controller_rule_action_type_execute__enum_size is returned for unknown types. + * + */ +#ifndef _di_controller_rule_action_type_to_action_execute_type_ + extern uint8_t controller_rule_action_type_to_action_execute_type(const uint8_t type) f_attribute_visibility_internal; +#endif // _di_controller_rule_action_type_to_action_execute_type_ + +/** * Get a string representing the rule action type. * * @param type @@ -94,6 +110,20 @@ extern "C" { #endif // _di_controller_rule_action_type_name_ /** + * Get a string representing the rule action execute type. + * + * @param type + * The rule action type execute code. + * + * @return + * The string with used > 0 on success. + * The string with used == 0 if no match was found. + */ +#ifndef _di_controller_rule_action_type_execute_name_ + extern f_string_static_t controller_rule_action_type_execute_name(const uint8_t type) f_attribute_visibility_internal; +#endif // _di_controller_rule_action_type_execute_name_ + +/** * Increase the size of the rule actions array by the specified amount, but only if necessary. * * This only increases size if the current used plus amount is greater than the currently allocated size. @@ -285,21 +315,6 @@ extern "C" { #endif // _di_controller_rule_item_error_print_execute_ /** - * Print an error or warning message related to the failed execution of some program or script for when the program or script is not found. - * - * @param print - * The error or warning print structure. - * @param script_is - * If TRUE, then this represents a script. - * If FALSE, then this represents a program. - * @param code - * The code returned by the executed program or script. - */ -#ifndef _di_controller_rule_item_error_print_execute_not_found_ - extern void controller_rule_item_error_print_execute_not_found(const fll_error_print_t print, const bool script_is, const f_string_t name) f_attribute_visibility_internal; -#endif // _di_controller_rule_item_error_print_execute_not_found_ - -/** * Print an error or warning message related to need/want/wish settings of some rule. * * @param print @@ -480,6 +495,31 @@ extern "C" { #endif // _di_controller_rule_execute_pid_with_ /** + * Determine whether or not an execute rule should be re-run, applying a delay as requested. + * + * @param global + * The global data. + * @param action + * The action type. + * @param process + * The process data for processing this rule. + * @param item + * The rule item being executed. + * + * @return + * A positive number to designate re-run. + * 0 to designate do not re-run. + * -1 to designate an error from nanosleep(), with errno set to values like: + * - EFAULT: Designates that there was a problem copying information from user space. + * - EINTR: Consider this having returned F_signal. + * - EINVAL: Consider this having returned F_status_set_error(F_parameter); + * -2 to designate exit due to signal/disabled thread. + */ +#ifndef _di_controller_rule_execute_rerun_ + extern int8_t controller_rule_execute_rerun(const controller_global_t global, const uint8_t action, controller_process_t *process, controller_rule_item_t *item) f_attribute_visibility_internal; +#endif // _di_controller_rule_execute_rerun_ + +/** * Construct an id from two distinct strings found within a single given source. * * @param global diff --git a/level_3/controller/c/private-thread.c b/level_3/controller/c/private-thread.c index 49e2f91..24e08ed 100644 --- a/level_3/controller/c/private-thread.c +++ b/level_3/controller/c/private-thread.c @@ -361,6 +361,9 @@ extern "C" { controller_thread_delete_simple(thread); controller_setting_delete_simple(setting); controller_main_delete(main); + + // According to the manpages, pthread_exit() calls exit(0), which is not good because a non-zero exit code may be returned. + exit(main->child); } } #endif // _di_controller_thread_process_ @@ -800,6 +803,11 @@ extern "C" { controller_setting_delete_simple(entry->global->setting); controller_main_delete(entry->global->main); + const int code = main->child; + + // According to the manpages, pthread_exit() calls exit(0), which is not good because a non-zero exit code may be returned. + exit(main->child); + return 0; } diff --git a/level_3/controller/data/build/settings b/level_3/controller/data/build/settings index 3486eaf..3715a1a 100644 --- a/level_3/controller/data/build/settings +++ b/level_3/controller/data/build/settings @@ -20,7 +20,7 @@ build_compiler gcc build_indexer ar build_language c build_libraries -lc -lcap -build_libraries-individual -lfll_control_group -lfll_error -lfll_execute -lfll_fss -lfll_path -lfll_print -lfll_program -lfl_console -lfl_control_group -lfl_conversion -lfl_directory -lfl_environment -lfl_fss -lfl_iki -lfl_print -lfl_status -lfl_string -lf_account -lf_capability -lf_color -lf_console -lf_control_group -lf_conversion -lf_directory -lf_environment -lf_file -lf_fss -lf_iki -lf_limit -lf_memory -lf_path -lf_pipe -lf_print -lf_signal -lf_string -lf_thread -lf_type_array -lf_utf +build_libraries-individual -lfll_control_group -lfll_error -lfll_execute -lfll_fss -lfll_path -lfll_print -lfll_program -lfl_console -lfl_control_group -lfl_conversion -lfl_directory -lfl_environment -lfl_fss -lfl_iki -lfl_print -lfl_status -lfl_string -lf_account -lf_capability -lf_color -lf_console -lf_control_group -lf_conversion -lf_directory -lf_environment -lf_execute -lf_file -lf_fss -lf_iki -lf_limit -lf_memory -lf_path -lf_pipe -lf_print -lf_signal -lf_string -lf_thread -lf_type_array -lf_utf build_libraries-level -lfll_2 -lfll_1 -lfll_0 build_libraries-monolithic -lfll build_sources_library controller.c private-common.c private-control.c private-controller.c private-entry.c private-rule.c private-thread.c diff --git a/level_3/controller/data/settings/example/entries/htop-command.entry b/level_3/controller/data/settings/example/entries/htop-command.entry new file mode 100644 index 0000000..9a15e99 --- /dev/null +++ b/level_3/controller/data/settings/example/entries/htop-command.entry @@ -0,0 +1,7 @@ +# fss-0005 + +setting: + mode program + +main: + start command htop diff --git a/level_3/controller/data/settings/example/rules/command/htop.rule b/level_3/controller/data/settings/example/rules/command/htop.rule new file mode 100644 index 0000000..af43951 --- /dev/null +++ b/level_3/controller/data/settings/example/rules/command/htop.rule @@ -0,0 +1,9 @@ +# fss-000d + +setting: + name "Run htop" + +command: + start htop + + rerun start success delay 3000 max 3 diff --git a/level_3/fake/data/build/settings b/level_3/fake/data/build/settings index 97f8075..8c3bc2d 100644 --- a/level_3/fake/data/build/settings +++ b/level_3/fake/data/build/settings @@ -20,7 +20,7 @@ build_compiler gcc build_indexer ar build_language c build_libraries -lc -lcap -build_libraries-individual -lfll_error -lfll_execute -lfll_file -lfll_fss -lfll_path -lfll_print -lfll_program -lfl_console -lfl_control_group -lfl_conversion -lfl_directory -lfl_environment -lfl_fss -lfl_iki -lfl_print -lfl_status -lfl_string -lf_account -lf_capability -lf_color -lf_console -lf_control_group -lf_conversion -lf_directory -lf_environment -lf_file -lf_fss -lf_iki -lf_limit -lf_memory -lf_path -lf_print -lf_signal -lf_string -lf_thread -lf_type_array -lf_utf +build_libraries-individual -lfll_error -lfll_execute -lfll_file -lfll_fss -lfll_path -lfll_print -lfll_program -lfl_console -lfl_control_group -lfl_conversion -lfl_directory -lfl_environment -lfl_fss -lfl_iki -lfl_print -lfl_status -lfl_string -lf_account -lf_capability -lf_color -lf_console -lf_control_group -lf_conversion -lf_directory -lf_environment -lf_execute -lf_file -lf_fss -lf_iki -lf_limit -lf_memory -lf_path -lf_print -lf_signal -lf_string -lf_thread -lf_type_array -lf_utf build_libraries-level -lfll_2 -lfll_1 -lfll_0 build_libraries-monolithic -lfll build_sources_library fake.c private-common.c private-fake.c private-clean.c private-build.c private-make.c private-print.c private-skeleton.c diff --git a/level_3/firewall/data/build/settings b/level_3/firewall/data/build/settings index beeecd1..db7b5e0 100644 --- a/level_3/firewall/data/build/settings +++ b/level_3/firewall/data/build/settings @@ -20,7 +20,7 @@ build_compiler gcc build_indexer ar build_language c build_libraries -lc -lcap -build_libraries-individual -lfll_error -lfll_execute -lfll_fss -lfll_print -lfll_program -lfl_console -lfl_control_group -lfl_conversion -lfl_environment -lfl_fss -lfl_print -lfl_status -lfl_string -lf_account -lf_capability -lf_color -lf_console -lf_control_group -lf_conversion -lf_directory -lf_environment -lf_file -lf_fss -lf_limit -lf_memory -lf_path -lf_pipe -lf_print -lf_signal -lf_string -lf_thread -lf_type_array -lf_utf +build_libraries-individual -lfll_error -lfll_execute -lfll_fss -lfll_print -lfll_program -lfl_console -lfl_control_group -lfl_conversion -lfl_environment -lfl_fss -lfl_print -lfl_status -lfl_string -lf_account -lf_capability -lf_color -lf_console -lf_control_group -lf_conversion -lf_directory -lf_environment -lf_execute -lf_file -lf_fss -lf_limit -lf_memory -lf_path -lf_pipe -lf_print -lf_signal -lf_string -lf_thread -lf_type_array -lf_utf build_libraries-level -lfll_2 -lfll_1 -lfll_0 build_libraries-monolithic -lfll build_sources_library firewall.c private-common.c private-firewall.c