From: Kevin Day Date: Fri, 18 Dec 2020 04:18:00 +0000 (-0600) Subject: Progress: controller program, working on execute script logic. X-Git-Tag: 0.5.2~10 X-Git-Url: https://git.kevux.org/?a=commitdiff_plain;h=f604e32efb2385ee6748171e2dfc0666cafe62cf;p=fll Progress: controller program, working on execute script logic. I have been tossing around how exactly I want to implement the execute with piped data. I have decided that I should clean up the parameters and redesign it to accept a few specialized structures. Before I do that, I wanted to confirm that the pipe to shell (such as bash) works as I expect it to. This tweaks the code to a temporary state where I can automatically execute some bash script with some string piped to it. This seems to work as expected and errors out as expected. I also need to write some asynchronous functions to help with managing asynchronous processes. This might require a new series of projects (f_asynchronous, fl_asynchronous, etc..). Note: "bash" was used as the default script, but I really need to provide some sort of configuration to change this. --- diff --git a/level_0/f_execute/c/execute-common.h b/level_0/f_execute/c/execute-common.h index b2201d1..5e5ed27 100644 --- a/level_0/f_execute/c/execute-common.h +++ b/level_0/f_execute/c/execute-common.h @@ -17,30 +17,34 @@ extern "C" { #endif /** - * A set containing the standard descriptors for use in pipes during the execution process. + * A structure for use in processing asynchronous execute behavior. * * Future versions may suppot stdwarn and stddebug if there ever is such a thing. * * Descriptors always use -1 to designate not used because 0 represent a valid descriptor. + * The process ID always uses -1 to designate no valid process ID is set. * - * input: the input file descriptor. - * output: the output file descriptor. - * error: the error file descriptor. + * input: the input file descriptor. + * output: the output file descriptor. + * error: the error file descriptor. + * process: the process ID whereas 0 represents a parent and anything greater than 0 represents a child. */ -#ifndef _di_f_execute_pipe_t_ +#ifndef _di_f_execute_asynchronous_t_ typedef struct { int input; int output; int error; - } f_execute_pipe_t; + int process; + } f_execute_asynchronous_t; - #define f_execute_pipe_t_initialize { -1, -1, -1 } + #define f_execute_asynchronous_t_initialize { -1, -1, -1, -1 } - #define f_execute_pipe_t_clear(set) \ + #define f_execute_asynchronous_t_clear(set) \ set.input = -1; \ set.output = -1; \ - set.error = -1; -#endif // _di_f_execute_pipe_t_ + set.error = -1; \ + set.process = -1; +#endif // _di_f_execute_asynchronous_t_ #ifdef __cplusplus } // extern "C" diff --git a/level_2/fll_execute/c/execute.c b/level_2/fll_execute/c/execute.c index da9c2a6..948e98a 100644 --- a/level_2/fll_execute/c/execute.c +++ b/level_2/fll_execute/c/execute.c @@ -130,7 +130,7 @@ extern "C" { #endif // _di_fll_execute_arguments_dynamic_add_set_ #ifndef _di_fll_execute_path_ - f_return_status fll_execute_path(const f_string_t program_path, const f_string_statics_t arguments, const f_signal_how_t *signals, f_execute_pipe_t * const pipe, int *result) { + f_return_status fll_execute_path(const f_string_t program_path, const f_string_statics_t arguments, const f_signal_how_t *signals, f_string_static_t * const pipe, int *result) { #ifndef _di_level_2_parameter_checking_ if (!result) return F_status_set_error(F_parameter); #endif // _di_level_2_parameter_checking_ @@ -163,7 +163,7 @@ extern "C" { #endif // _di_fll_execute_path_ #ifndef _di_fll_execute_path_environment_ - f_return_status fll_execute_path_environment(const f_string_t program_path, const f_string_statics_t arguments, const f_string_statics_t names, const f_string_statics_t values, const f_signal_how_t *signals, f_execute_pipe_t * const pipe, int *result) { + f_return_status fll_execute_path_environment(const f_string_t program_path, const f_string_statics_t arguments, const f_string_statics_t names, const f_string_statics_t values, const f_signal_how_t *signals, f_string_static_t * const pipe, int *result) { #ifndef _di_level_2_parameter_checking_ if (!result) return F_status_set_error(F_parameter); #endif // _di_level_2_parameter_checking_ @@ -194,7 +194,7 @@ extern "C" { #endif // _di_fll_execute_path_environment_ #ifndef _di_fll_execute_program_ - f_return_status fll_execute_program(const f_string_t program_name, const f_string_statics_t arguments, const f_signal_how_t *signals, f_execute_pipe_t * const pipe, int *result) { + f_return_status fll_execute_program(const f_string_t program_name, const f_string_statics_t arguments, const f_signal_how_t *signals, f_string_static_t * const pipe, int *result) { #ifndef _di_level_2_parameter_checking_ if (!result) return F_status_set_error(F_parameter); #endif // _di_level_2_parameter_checking_ @@ -216,7 +216,7 @@ extern "C" { #endif // _di_fll_execute_program_ #ifndef _di_fll_execute_program_environment_ - f_return_status fll_execute_program_environment(const f_string_t program_name, const f_string_statics_t arguments, const f_string_statics_t names, const f_string_statics_t values, const f_signal_how_t *signals, f_execute_pipe_t * const pipe, int *result) { + f_return_status fll_execute_program_environment(const f_string_t program_name, const f_string_statics_t arguments, const f_string_statics_t names, const f_string_statics_t values, const f_signal_how_t *signals, f_string_static_t * const pipe, int *result) { #ifndef _di_level_2_parameter_checking_ if (!result) return F_status_set_error(F_parameter); #endif // _di_level_2_parameter_checking_ diff --git a/level_2/fll_execute/c/execute.h b/level_2/fll_execute/c/execute.h index 8c58852..64e7de9 100644 --- a/level_2/fll_execute/c/execute.h +++ b/level_2/fll_execute/c/execute.h @@ -334,14 +334,8 @@ extern "C" { * This does validate that the program path exists. * * This does not call exit() when the child process exits. - * Instead, this returns F_child and assigns the child's return code to result. - * The caller is expected to handle the appropriate exit procedures and memory deallocation. - * - * When the passed pipe parameter is not NULL, this function is effectively asynchronous and will not block. - * The caller is then expected to handle all read/write operations to/from the pipe. - * The caller is expected to appropriately call waitpid() or similar as needed. - * The caller is expected to handle both parent and child process (status is F_parent for parent and F_child for child.). - * When the passed paremeter is NULL, then this function is effectively synchronous and blocks until child exits. + * Instead, this returns F_child and assigns the child's return code to result for the child process. + * The caller is expected to handle the appropriate exit procedures and memory deallocation for the child process. * * @param program_path * The entire path to the program. @@ -351,9 +345,7 @@ extern "C" { * (optional) A pointer to the set of signals. * Set to 0 to disable. * @param pipe - * (optional) A pointer to the set of pipe desciptors (I/O) to be used by the child process. - * When a non-zero address, the child process will assign these as the standard I/O file descriptors for piping to/from the parent/child. - * For each pipe, setting a value of -1 means to use the default pipe. + * (optional) A pointer to a string to pipe as standard input to the child process. * Set to 0 to disable. * @param result * The code returned after finishing execution of program_path. @@ -361,44 +353,39 @@ extern "C" { * @return * F_none on success. * F_child on success but this is the child thread. - * F_parent on success but this is the parent thread and pipe is non-zero (function is not blocking). * F_failure (with error bit) on execution failure. * F_fork (with error bit) on fork failure. + * F_pipe (with error bit) on pipe failure. * * Errors (with error bit) from: f_file_exists(). * Errors (with error bit) from: f_signal_set_handle(). * + * @see close() * @see dup2() * @see execv() - * @see exit() + * @see execvp() * @see fork() - * @see memcpy() - * @see strnlen() + * @see pipe() * @see waitpid() * * @see f_file_exists() * @see f_signal_set_handle() */ #ifndef _di_fll_execute_path_ - extern f_return_status fll_execute_path(const f_string_t program_path, const f_string_statics_t arguments, const f_signal_how_t *signals, f_execute_pipe_t * const pipe, int *result); + extern f_return_status fll_execute_path(const f_string_t program_path, const f_string_statics_t arguments, const f_signal_how_t *signals, f_string_static_t * const pipe, int *result); #endif // _di_fll_execute_path_ /** * Execute a program given some path + program name (such as "/bin/bash"). * - * This does validate that the program path exists. - * + * Uses the provided environment array to designate the environment for the program being executed. * The environment is defined by the names and values pair. * - * This does not call exit() when the child process exits. - * Instead, this returns F_child and assigns the child's return code to result. - * The caller is expected to handle the appropriate exit procedures and memory deallocation. + * This does validate that the program path exists. * - * When the passed pipe parameter is not NULL, this function is effectively asynchronous and will not block. - * The caller is then expected to handle all read/write operations to/from the pipe. - * The caller is expected to appropriately call waitpid() or similar as needed. - * The caller is expected to handle both parent and child process (status is F_parent for parent and F_child for child.). - * When the passed paremeter is NULL, then this function is effectively synchronous and blocks until child exits. + * This does not call exit() when the child process exits. + * Instead, this returns F_child and assigns the child's return code to result for the child process. + * The caller is expected to handle the appropriate exit procedures and memory deallocation for the child process. * * @param program_path * The entire path to the program. @@ -416,9 +403,7 @@ extern "C" { * (optional) A pointer to the set of signals. * Set to 0 to disable. * @param pipe - * (optional) A pointer to the set of pipe desciptors (I/O) to be used by the child process. - * When a non-zero address, the child process will assign these as the standard I/O file descriptors for piping to/from the parent/child. - * For each pipe, setting a value of -1 means to use the default pipe. + * (optional) A pointer to a string to pipe as standard input to the child process. * Set to 0 to disable. * @param result * The code returned after finishing execution of program_path. @@ -426,19 +411,21 @@ extern "C" { * @return * F_none on success. * F_child on success but this is the child thread. - * F_parent on success but this is the parent thread and pipe is non-zero (function is not blocking). * F_failure (with error bit) on execution failure. * F_fork (with error bit) on fork failure. + * F_pipe (with error bit) on pipe failure. * * Errors (with error bit) from: f_environment_set_dynamic(). * Errors (with error bit) from: f_file_exists(). * Errors (with error bit) from: f_signal_set_handle(). * + * @see close() * @see clearenv() * @see dup2() - * @see execv() + * @see execvp() * @see fork() * @see memcpy() + * @see pipe() * @see strnlen() * @see waitpid() * @@ -447,7 +434,7 @@ extern "C" { * @see f_signal_set_handle() */ #ifndef _di_fll_execute_path_environment_ - extern f_return_status fll_execute_path_environment(const f_string_t program_path, const f_string_statics_t arguments, const f_string_statics_t names, const f_string_statics_t values, const f_signal_how_t *signals, f_execute_pipe_t * const pipe, int *result); + extern f_return_status fll_execute_path_environment(const f_string_t program_path, const f_string_statics_t arguments, const f_string_statics_t names, const f_string_statics_t values, const f_signal_how_t *signals, f_string_static_t * const pipe, int *result); #endif // _di_fll_execute_path_environment_ /** @@ -456,14 +443,8 @@ extern "C" { * This does not validate the path to the program. * * This does not call exit() when the child process exits. - * Instead, this returns F_child and assigns the child's return code to result. - * The caller is expected to handle the appropriate exit procedures and memory deallocation. - * - * When the passed pipe parameter is not NULL, this function is effectively asynchronous and will not block. - * The caller is then expected to handle all read/write operations to/from the pipe. - * The caller is expected to appropriately call waitpid() or similar as needed. - * The caller is expected to handle both parent and child process (status is F_parent for parent and F_child for child.). - * When the passed paremeter is NULL, then this function is effectively synchronous and blocks until child exits. + * Instead, this returns F_child and assigns the child's return code to result for the child process. + * The caller is expected to handle the appropriate exit procedures and memory deallocation for the child process. * * @param program_name * The name of the program. @@ -473,9 +454,7 @@ extern "C" { * (optional) A pointer to the set of signals. * Set to 0 to disable. * @param pipe - * (optional) A pointer to the set of pipe desciptors (I/O) to be used by the child process. - * When a non-zero address, the child process will assign these as the standard I/O file descriptors for piping to/from the parent/child. - * For each pipe, setting a value of -1 means to use the default pipe. + * (optional) A pointer to a string to pipe as standard input to the child process. * Set to 0 to disable. * @param result * The code returned after finishing execution of program. @@ -483,44 +462,39 @@ extern "C" { * @return * F_none on success. * F_child on success but this is the child thread. - * F_parent on success but this is the parent thread and pipe is non-zero (function is not blocking). * F_failure (with error bit) on execution failure. * F_fork (with error bit) on fork failure. + * F_pipe (with error bit) on pipe failure. * * Errors (with error bit) from: f_file_exists(). * Errors (with error bit) from: f_signal_set_handle(). * + * @see close() * @see dup2() + * @see execv() * @see execvp() * @see fork() - * @see strnlen() + * @see pipe() * @see waitpid() * * @see f_file_exists() * @see f_signal_set_handle() */ #ifndef _di_fll_execute_program_ - extern f_return_status fll_execute_program(const f_string_t program_name, const f_string_statics_t arguments, const f_signal_how_t *signals, f_execute_pipe_t * const pipe, int *result); + extern f_return_status fll_execute_program(const f_string_t program_name, const f_string_statics_t arguments, const f_signal_how_t *signals, f_string_static_t * const pipe, int *result); #endif // _di_fll_execute_program_ /** * Execute a program given by name found in the PATH environment (such as "bash"). * - * Uses the provided environment array to designate the environment for the called program. - * - * This does validate the path to the program because it completes the path to the program. - * This is done because the PATH environment will get cleared or may be set differently. - * Execution of program_name is done using the PATH environment prior to clearing and reassigning the environment variables. + * Uses the provided environment array to designate the environment for the program being executed. + * The environment is defined by the names and values pair. * - * Unlike the execv() family of functions, this does not call exit() when the child process exits. - * Instead, this returns F_child and assigns the child's return code to result. - * The caller is expected to handle the appropriate exit procedures and memory deallocation. + * This does not validate the path to the program. * - * When the passed pipe parameter is not NULL, this function is effectively asynchronous and will not block. - * The caller is then expected to handle all read/write operations to/from the pipe. - * The caller is expected to appropriately call waitpid() or similar as needed. - * The caller is expected to handle both parent and child process (status is F_parent for parent and F_child for child.). - * When the passed paremeter is NULL, then this function is effectively synchronous and blocks until child exits. + * This does not call exit() when the child process exits. + * Instead, this returns F_child and assigns the child's return code to result for the child process. + * The caller is expected to handle the appropriate exit procedures and memory deallocation for the child process. * * @param program_name * The name of the program. @@ -538,9 +512,7 @@ extern "C" { * (optional) A pointer to the set of signals. * Set to 0 to disable. * @param pipe - * (optional) A pointer to the set of pipe desciptors (I/O) to be used by the child process. - * When a non-zero address, the child process will assign these as the standard I/O file descriptors for piping to/from the parent/child. - * For each pipe, setting a value of -1 means to use the default pipe. + * (optional) A pointer to a string to pipe as standard input to the child process. * Set to 0 to disable. * @param result * The code returned after finishing execution of program. @@ -548,9 +520,9 @@ extern "C" { * @return * F_none on success. * F_child on success but this is the child thread. - * F_parent on success but this is the parent thread and pipe is non-zero (function is not blocking). * F_failure (with error bit) on execution failure. * F_fork (with error bit) on fork failure. + * F_pipe (with error bit) on pipe failure. * * Errors (with error bit) from: f_environment_get(). * Errors (with error bit) from: f_file_exists(). @@ -561,11 +533,13 @@ extern "C" { * Errors (with error bit) from: fl_string_append(). * Errors (with error bit) from: fl_string_dynamic_terminate(). * + * @see close() * @see clearenv() * @see dup2() * @see execvp() * @see fork() * @see memcpy() + * @see pipe() * @see strnlen() * @see waitpid() * @@ -577,7 +551,7 @@ extern "C" { * @see fl_string_dynamic_terminate() */ #ifndef _di_fll_execute_program_environment_ - extern f_return_status fll_execute_program_environment(const f_string_t program_name, const f_string_statics_t arguments, const f_string_statics_t names, const f_string_statics_t values, const f_signal_how_t *signals, f_execute_pipe_t * const pipe, int *result); + extern f_return_status fll_execute_program_environment(const f_string_t program_name, const f_string_statics_t arguments, const f_string_statics_t names, const f_string_statics_t values, const f_signal_how_t *signals, f_string_static_t * const pipe, int *result); #endif // _di_fll_execute_program_environment_ #ifdef __cplusplus diff --git a/level_2/fll_execute/c/private-execute.c b/level_2/fll_execute/c/private-execute.c index 97dab13..4f0cc5f 100644 --- a/level_2/fll_execute/c/private-execute.c +++ b/level_2/fll_execute/c/private-execute.c @@ -111,42 +111,54 @@ extern "C" { #endif // !defined(_di_fll_execute_arguments_add_parameter_) || !defined(_di_fll_execute_arguments_add_parameter_set_) || !defined(_di_fll_execute_arguments_dynamic_add_parameter_) || !defined(_di_fll_execute_arguments_dynamic_add_parameter_set_) #if !defined(_di_fll_execute_path_) || !defined(_di_fll_execute_program_) - f_return_status private_fll_execute_fork(const f_string_t program_path, const f_string_t fixed_arguments[], const bool program_is, const f_signal_how_t *signals, f_execute_pipe_t * const pipe, int *result) { + f_return_status private_fll_execute_fork(const f_string_t program_path, const f_string_t fixed_arguments[], const bool program_is, const f_signal_how_t *signals, f_string_static_t * const pipe_data, int *result) { + + int descriptors[2] = { -1, -1 }; + + if (pipe_data) { + if (pipe(descriptors) == -1) { + return F_status_set_error(F_pipe); + } + } const pid_t process_id = fork(); if (process_id < 0) { + + if (pipe_data) { + close(descriptors[0]); + close(descriptors[1]); + } + return F_status_set_error(F_fork); } if (process_id) { - if (pipe) { - return F_parent; - } + + // close the read pipe for the parent. + close(descriptors[0]); } else { + // close the write pipe for the child. + close(descriptors[1]); + if (signals) { f_signal_set_handle(SIG_BLOCK, &signals->block); f_signal_set_handle(SIG_UNBLOCK, &signals->block_not); } - if (pipe) { - if (pipe->input != -1) { - dup2(pipe->input, f_type_descriptor_input); - } - - if (pipe->output != -1) { - dup2(pipe->input, f_type_descriptor_output); - } - - if (pipe->error != -1) { - dup2(pipe->error, f_type_descriptor_error); - } + if (pipe_data) { + dup2(descriptors[0], f_type_descriptor_input); } const int code = program_is ? execvp(program_path, fixed_arguments) : execv(program_path, fixed_arguments); + // close the write pipe for the child when done. + if (pipe_data) { + close(descriptors[0]); + } + if (result) { *result = code; } @@ -154,6 +166,16 @@ extern "C" { return F_child; } + // write all data, if child doesn't read this could block until child closes the pipe. + if (pipe_data) { + const f_file_t file = f_macro_file_t_initialize(0, descriptors[1], f_file_flag_write_only); + + f_file_write(file, *pipe_data, 0); + + // close the write pipe for the parent when finished writing. + close(descriptors[1]); + } + // have the parent wait for the child process to finish. waitpid(process_id, result, WUNTRACED | WCONTINUED); @@ -170,20 +192,38 @@ extern "C" { #endif // !defined(_di_fll_execute_path_) || !defined(_di_fll_execute_program_) #if !defined(_di_fll_execute_path_environment_) || !defined(_di_fll_execute_program_environment_) - f_return_status private_fll_execute_fork_environment(const f_string_t program_path, const f_string_t fixed_arguments[], const bool program_is, const f_string_statics_t names, const f_string_statics_t values, const f_signal_how_t *signals, f_execute_pipe_t * const pipe, int *result) { + f_return_status private_fll_execute_fork_environment(const f_string_t program_path, const f_string_t fixed_arguments[], const bool program_is, const f_string_statics_t names, const f_string_statics_t values, const f_signal_how_t *signals, f_string_static_t * const pipe_data, int *result) { + + int descriptors[2] = { -1, -1 }; + + if (pipe_data) { + if (pipe(descriptors) == -1) { + return F_status_set_error(F_pipe); + } + } const pid_t process_id = fork(); if (process_id < 0) { + + if (pipe_data) { + close(descriptors[0]); + close(descriptors[1]); + } + return F_status_set_error(F_fork); } if (process_id) { - if (pipe) { - return F_parent; - } + + // close the read pipe for the parent. + close(descriptors[0]); } else { + + // close the write pipe for the child. + close(descriptors[1]); + if (signals) { f_signal_set_handle(SIG_BLOCK, &signals->block); f_signal_set_handle(SIG_UNBLOCK, &signals->block_not); @@ -195,22 +235,17 @@ extern "C" { f_environment_set_dynamic(names.array[i], values.array[i], F_true); } // for - if (pipe) { - if (pipe->input != -1) { - dup2(pipe->input, f_type_descriptor_input); - } - - if (pipe->output != -1) { - dup2(pipe->input, f_type_descriptor_output); - } - - if (pipe->error != -1) { - dup2(pipe->error, f_type_descriptor_error); - } + if (pipe_data) { + dup2(descriptors[0], f_type_descriptor_input); } const int code = program_is ? execvp(program_path, fixed_arguments) : execv(program_path, fixed_arguments); + // close the write pipe for the child when done. + if (pipe_data) { + close(descriptors[0]); + } + if (result) { *result = code; } @@ -218,6 +253,16 @@ extern "C" { return F_child; } + // write all data, if child doesn't read this could block until child closes the pipe. + if (pipe_data) { + const f_file_t file = f_macro_file_t_initialize(0, descriptors[1], f_file_flag_write_only); + + f_file_write(file, *pipe_data, 0); + + // close the write pipe for the parent when finished writing. + close(descriptors[1]); + } + // have the parent wait for the child process to finish. waitpid(process_id, result, WUNTRACED | WCONTINUED); diff --git a/level_2/fll_execute/c/private-execute.h b/level_2/fll_execute/c/private-execute.h index ea9dd2d..e024643 100644 --- a/level_2/fll_execute/c/private-execute.h +++ b/level_2/fll_execute/c/private-execute.h @@ -117,10 +117,8 @@ extern "C" { * @param set_signal * (optional) A pointer to the set of signals. * Set to 0 to disable. - * @param pipe - * (optional) A pointer to the set of pipe desciptors (I/O) to be used by the child process. - * When a non-zero address, the child process will assign these as the standard I/O file descriptors for piping to/from the parent/child. - * For each pipe, setting a value of -1 means to use the default pipe. + * @param pipe_data + * (optional) A pointer to a string to pipe as standard input to the child process. * Set to 0 to disable. * @param result * The code returned after finishing execution of program_path. @@ -128,17 +126,23 @@ extern "C" { * @return * F_none on success. * F_child on success but this is the child thread. - * F_parent on success but this is the parent thread and pipe is non-zero (function is not blocking). * F_failure (with error bit) on execution failure. * F_fork (with error bit) on fork failure. + * F_pipe (with error bit) on pipe failure. * + * @see close() + * @see dup2() * @see execv() * @see execvp() + * @see fork() + * @see pipe() + * @see waitpid() + * * @see fll_execute_path() * @see fll_execute_program() */ #if !defined(_di_fll_execute_path_) || !defined(_di_fll_execute_program_) - extern f_return_status private_fll_execute_fork(const f_string_t program_path, const f_string_t fixed_arguments[], const bool program_is, const f_signal_how_t *signals, f_execute_pipe_t * const pipe, int *result) f_gcc_attribute_visibility_internal; + extern f_return_status private_fll_execute_fork(const f_string_t program_path, const f_string_t fixed_arguments[], const bool program_is, const f_signal_how_t *signals, f_string_static_t * const pipe_data, int *result) f_gcc_attribute_visibility_internal; #endif // !defined(_di_fll_execute_path_) || !defined(_di_fll_execute_program_) /** @@ -162,10 +166,8 @@ extern "C" { * @param signals * (optional) A pointer to the set of signals. * Set to 0 to disable. - * @param pipe - * (optional) A pointer to the set of pipe desciptors (I/O) to be used by the child process. - * When a non-zero address, the child process will assign these as the standard I/O file descriptors for piping to/from the parent/child. - * For each pipe, setting a value of -1 means to use the default pipe. + * @param pipe_data + * (optional) A pointer to a string to pipe as standard input to the child process. * Set to 0 to disable. * @param result * The code returned after finishing execution of program_path. @@ -173,17 +175,25 @@ extern "C" { * @return * F_none on success. * F_child on success but this is the child thread. - * F_parent on success but this is the parent thread and pipe is non-zero (function is not blocking). * F_failure (with error bit) on execution failure. * F_fork (with error bit) on fork failure. + * F_pipe (with error bit) on pipe failure. + * + * @see close() + * @see clearenv() + * @see dup2() + * @see execvp() + * @see fork() + * @see memcpy() + * @see pipe() + * @see strnlen() + * @see waitpid() * - * @see execv() - * @see execvpe() * @see fll_execute_path_environment() * @see fll_execute_program_environment() */ #if !defined(_di_fll_execute_path_environment_) || !defined(_di_fll_execute_program_environment_) - extern f_return_status private_fll_execute_fork_environment(const f_string_t program_path, const f_string_t fixed_arguments[], const bool program_is, const f_string_statics_t names, const f_string_statics_t values, const f_signal_how_t *signals, f_execute_pipe_t * const pipe, int *result) f_gcc_attribute_visibility_internal; + extern f_return_status private_fll_execute_fork_environment(const f_string_t program_path, const f_string_t fixed_arguments[], const bool program_is, const f_string_statics_t names, const f_string_statics_t values, const f_signal_how_t *signals, f_string_static_t * const pipe_data, int *result) f_gcc_attribute_visibility_internal; #endif // !defined(_di_fll_execute_path_environment_) || !defined(_di_fll_execute_program_environment_) /** diff --git a/level_3/controller/c/controller.h b/level_3/controller/c/controller.h index 559018b..10ba05e 100644 --- a/level_3/controller/c/controller.h +++ b/level_3/controller/c/controller.h @@ -50,6 +50,7 @@ // fll-2 includes #include +#include #include #include #include diff --git a/level_3/controller/c/private-common.h b/level_3/controller/c/private-common.h index bd0ec64..358f9fc 100644 --- a/level_3/controller/c/private-common.h +++ b/level_3/controller/c/private-common.h @@ -16,6 +16,7 @@ extern "C" { #define controller_string_action "action" #define controller_string_actions "actions" #define controller_string_asynchronous "asynchronous" + #define controller_string_bash "bash" #define controller_string_create "create" #define controller_string_command "command" #define controller_string_consider "consider" @@ -66,6 +67,7 @@ extern "C" { #define controller_string_action_length 6 #define controller_string_actions_length 7 #define controller_string_asynchronous_length 12 + #define controller_string_bash_length 4 #define controller_string_create_length 6 #define controller_string_command_length 7 #define controller_string_consider_length 8 diff --git a/level_3/controller/c/private-rule.c b/level_3/controller/c/private-rule.c index 8b3504b..44c40fc 100644 --- a/level_3/controller/c/private-rule.c +++ b/level_3/controller/c/private-rule.c @@ -401,45 +401,43 @@ extern "C" { #endif // _di_controller_rule_error_need_want_wish_print_ #ifndef _di_controller_rule_execute_ - f_return_status controller_rule_execute(const controller_cache_t cache, const f_array_length_t index, const uint8_t action, controller_data_t *data, controller_setting_t *setting) { + f_return_status controller_rule_execute(const controller_cache_t cache, const f_array_length_t index, const uint8_t type, controller_data_t *data, controller_setting_t *setting) { + f_status_t status = F_none; f_array_length_t i = 0; f_array_length_t j = 0; f_array_length_t k = 0; - controller_rule_item_t *rule_item = 0; - controller_rule_action_t *rule_action = 0; + controller_rule_item_t *item = 0; + controller_rule_action_t *action = 0; for (i = 0; i < setting->rules.array[index].items.used; ++i) { if (setting->rules.array[index].items.array[i].type == controller_rule_item_type_setting) continue; - rule_item = &setting->rules.array[index].items.array[i]; + item = &setting->rules.array[index].items.array[i]; - for (j = 0; j < rule_item->actions.used; ++j) { + for (j = 0; j < item->actions.used; ++j) { - if (rule_item->actions.array[j].type != action) continue; + if (item->actions.array[j].type != type) continue; - rule_action = &rule_item->actions.array[j]; + action = &item->actions.array[j]; + status = F_none; - if (rule_item->type == controller_rule_item_type_command) { - if (rule_action->method == controller_rule_action_method_extended) { + if (item->type == controller_rule_item_type_command) { + if (action->method == controller_rule_action_method_extended) { // @todo } else { // @todo extended list execution. } } - else if (rule_item->type == controller_rule_item_type_script) { - if (rule_action->method == controller_rule_action_method_extended) { - // @todo - } - else { - // @todo extended list execution. - } + else if (item->type == controller_rule_item_type_script) { + status = controller_rule_execute_script(*action, 0, data); + if (F_status_is_error(status)) break; } - else if (rule_item->type == controller_rule_item_type_service) { - if (rule_action->method == controller_rule_action_method_extended) { + else if (item->type == controller_rule_item_type_service) { + if (action->method == controller_rule_action_method_extended) { // @todo } else { @@ -450,39 +448,46 @@ extern "C" { // unknown, just ignore for now. (@todo print a warning when in debug mode.) continue; } + + if (status == F_child || status == F_signal) break; } // for + + if (status == F_child || status == F_signal) break; } // for - return F_none; + return status; } #endif // _di_controller_rule_execute_ #ifndef _di_controller_rule_execute_script_ - f_return_status controller_rule_execute_script(const controller_rule_action_t action, controller_data_t *data) { + f_return_status controller_rule_execute_script(const controller_rule_action_t action, const uint8_t options, controller_data_t *data) { // child processes should receive all signals, without blocking. f_signal_how_t signals = f_signal_how_t_initialize; f_signal_set_empty(&signals.block); f_signal_set_fill(&signals.block_not); - f_execute_pipe_t pipe = f_execute_pipe_t_initialize; + int result = 0; - f_status_t status = F_none; - //f_status_t status = fll_execute_program_environment(program.string, arguments, environment.names, environment.values, &signals, &pipe, &data->child); + const f_string_dynamics_t arguments = f_string_dynamics_t_initialize; + + // @todo script program (such as: "bash") should be configurable somehow (a new entry setting? a new rule setting? both?). + // @todo have the environment variables built before executing the script and then instead call fll_execute_program_environment() for all execute functions (with environment.names, environment.values). + f_status_t status = fll_execute_program(controller_string_bash, arguments, &signals, action.parameters.used ? &action.parameters.array[0] : 0, &result); if (status == F_child) { - // @todo wait for parent pipe. - // @todo how do I get status code? there will likely need to be more changes to fll_execute_program_environment()... + data->child = result; + return F_child; } - // parent process should print to the pipe. - // @todo handle errors, print messages, etc.. - if (data->child != 0) { + if (result != 0) { status = F_status_set_error(F_failure); } else if (F_status_is_error(status)) { + fll_error_print(data->error, F_status_set_fine(status), "fll_execute_program_environment", F_true); + return status; } data->child = 0; @@ -1103,6 +1108,7 @@ extern "C" { if (F_status_is_error(status)) { fll_error_print(data->error, F_status_set_fine(status), "controller_rule_execute", F_true); + controller_rule_error_print(data->error, *cache, F_true); } if (status == F_child) { diff --git a/level_3/controller/c/private-rule.h b/level_3/controller/c/private-rule.h index c86bf37..1d2c705 100644 --- a/level_3/controller/c/private-rule.h +++ b/level_3/controller/c/private-rule.h @@ -175,7 +175,7 @@ extern "C" { * A structure for containing and caching relevant data. * @param index * The position in the setting.rules array representing the rule to simulate. - * @param action + * @param type * The action to perform based on the action type codes. * * Only subset of the action type codes are supported: @@ -199,10 +199,40 @@ extern "C" { * On failure, the individual status for the rule is set to an appropriate error status. */ #ifndef _di_controller_rule_execute_ - extern f_return_status controller_rule_execute(const controller_cache_t cache, const f_array_length_t index, const uint8_t action, controller_data_t *data, controller_setting_t *setting) f_gcc_attribute_visibility_internal; + extern f_return_status controller_rule_execute(const controller_cache_t cache, const f_array_length_t index, const uint8_t type, controller_data_t *data, controller_setting_t *setting) f_gcc_attribute_visibility_internal; #endif // _di_controller_rule_execute_ /** + * Perform an execution of the given rule for the script execution type. + * + * @param action + * The action to perform based on the action type codes. + * + * Only subset of the action type codes are supported: + * - controller_rule_action_type_kill + * - controller_rule_action_type_reload + * - controller_rule_action_type_restart + * - controller_rule_action_type_start + * - controller_rule_action_type_stop + * @param options + * The execute options. + * @param data + * The program data. + * + * @return + * F_none on success. + * F_child on child process exiting. + * F_signal on (exit) signal received. + * + * Errors (with error bit) from: fll_execute_program(). + * + * @see fll_execute_program() + */ +#ifndef _di_controller_rule_execute_script_ + extern f_return_status controller_rule_execute_script(const controller_rule_action_t action, const uint8_t options, controller_data_t *data) f_gcc_attribute_visibility_internal; +#endif // _di_controller_rule_execute_script_ + +/** * Search the already loaded rules to see if one is found. * * @param data diff --git a/level_3/controller/data/build/dependencies b/level_3/controller/data/build/dependencies index 32305e2..1011460 100644 --- a/level_3/controller/data/build/dependencies +++ b/level_3/controller/data/build/dependencies @@ -25,6 +25,7 @@ fl_iki fl_string fl_type fll_error +fll_execute fll_fss fll_path fll_program diff --git a/level_3/controller/data/build/settings b/level_3/controller/data/build/settings index 95f70d8..82788d3 100644 --- a/level_3/controller/data/build/settings +++ b/level_3/controller/data/build/settings @@ -19,7 +19,7 @@ build_compiler gcc build_indexer ar build_language c build_libraries -lc -build_libraries-individual -lfll_error -lfll_fss -lfll_path -lfll_program -lfll_status -lfl_color -lfl_console -lfl_conversion -lfl_fss -lfl_iki -lfl_status -lfl_string -lfl_type -lf_console -lf_conversion -lf_directory -lf_file -lf_fss -lf_iki -lf_memory -lf_path -lf_pipe -lf_print -lf_signal -lf_utf +build_libraries-individual -lfll_error -lfll_execute -lfll_fss -lfll_path -lfll_program -lfll_status -lfl_color -lfl_console -lfl_conversion -lfl_environment -lfl_fss -lfl_iki -lfl_status -lfl_string -lfl_type -lf_console -lf_conversion -lf_directory -lf_environment -lf_file -lf_fss -lf_iki -lf_memory -lf_path -lf_pipe -lf_print -lf_signal -lf_utf build_libraries-level -lfll_2 -lfll_1 -lfll_0 build_libraries-monolithic -lfll build_sources_library controller.c private-control.c private-controller.c private-entry.c private-rule.c