diff --git a/src/Sproc/Sproc.cpp b/src/Sproc/Sproc.cpp index 1eb24dc..38774a9 100644 --- a/src/Sproc/Sproc.cpp +++ b/src/Sproc/Sproc.cpp @@ -1,15 +1,26 @@ #include "Sproc.h" -#include -#include -#include -#include -#include "../Logger/Logger.h" -#include "errno.h" -#include -#include "fcntl.h" #define PARENT default +enum PIPE_FILE_DESCRIPTORS { + READ_END = 0, + WRITE_END = 1 +}; + +enum READ_RESULTS { + READ_EOF = 0, + READ_PIPEOPEN_O_NONBLOCK = -1 +}; + +enum FORK_STATES { + FORK_FAILURE = -1, + CHILD = 0 +}; + +/* ------------------ + * HELPERS + * ------------------ */ + // converts username to UID // returns false on failure int username_to_uid( std::string username, int & uid ) @@ -77,33 +88,7 @@ int teebuf::sync() teestream::teestream(std::ostream &o1, std::ostream &o2) : std::ostream( &tbuf), tbuf( o1.rdbuf(), o2.rdbuf() ) {} -enum PIPE_FILE_DESCRIPTORS { - READ_END = 0, - WRITE_END = 1 -}; - -enum READ_RESULTS { - READ_EOF = 0, - READ_PIPEOPEN_O_NONBLOCK = -1 -}; - - -enum SPROC_RETURN_CODES { - SUCCESS = true, - UID_NOT_FOUND = -404, - GID_NOT_FOUND = -405, - SET_GID_FAILED = -401, - SET_UID_FAILED = -404, - EXEC_FAILURE_GENERAL = -666, - DUP2_FAILED = -999, - PIPE_FAILED = -998 -}; - -enum FORK_STATES { - FORK_FAILURE = -1, - CHILD = 0 -}; - +// SET PROCESS TO A CERTAIN IDENTITY CONTEXT int set_identity_context( std::string task_name, std::string user_name, std::string group_name, Logger slog ) { // the UID and GID for the username and groupname provided for context setting int context_user_id; @@ -180,7 +165,6 @@ int Sproc::execute(std::string shell, std::string environment_file, std::string // in as hands off a way as possible with as few assumptions as possible, while still doing this in a somewhat C++-y // way. - // set up the "Tee" with the parent std::string child_stdout_log_path = "./stdout.log"; std::string child_stderr_log_path = "./stderr.log"; @@ -191,17 +175,14 @@ int Sproc::execute(std::string shell, std::string environment_file, std::string stdout_log.open( child_stdout_log_path.c_str(), std::ofstream::out | std::ofstream::app ); stderr_log.open( child_stderr_log_path.c_str(), std::ofstream::out | std::ofstream::app ); - // avoid cyclic dependencies between stdout and tee_out std::ostream tmp_stdout( std::cout.rdbuf() ); std::ostream tmp_stderr( std::cerr.rdbuf() ); - // writing to this ostream derivative will write to stdout log file and std::cout teestream tee_out(tmp_stdout, stdout_log); teestream tee_err(tmp_stderr, stderr_log); - // pop the cout/cerr buffers to the appropriate Tees' buffers // These cause a segfault when used with the I/O redirection happening around fork, pipe, dup2, execl... @@ -209,7 +190,6 @@ int Sproc::execute(std::string shell, std::string environment_file, std::string //std::cerr.rdbuf( tee_err.rdbuf() ); // ....and I don't know why. - // build the command to execute in the shell std::string sourcer = ". " + environment_file + " && " + command; // Show the user a debug print of what is going to be executed in the shell. @@ -246,6 +226,7 @@ int Sproc::execute(std::string shell, std::string environment_file, std::string { // fork failed slog.log(E_FATAL, "[ '" + task_name + "' ] Fork Failed."); + exit( FORK_FAILED ); break; } case FORK_STATES::CHILD: @@ -256,12 +237,11 @@ int Sproc::execute(std::string shell, std::string environment_file, std::string while ((dup2(child_stdout_pipe[WRITE_END], STDOUT_FILENO) == -1) && (errno == EINTR)) {} while ((dup2(child_stderr_pipe[WRITE_END], STDERR_FILENO) == -1) && (errno == EINTR)) {} - close(child_stdout_pipe[WRITE_END] ); - close(child_stdout_pipe[READ_END] ); - - close(child_stderr_pipe[WRITE_END] ); - close(child_stderr_pipe[READ_END] ); + close( child_stdout_pipe[WRITE_END] ); + close( child_stdout_pipe[READ_END] ); + close( child_stderr_pipe[WRITE_END] ); + close( child_stderr_pipe[READ_END] ); slog.log(E_INFO, "[ '" + task_name + "' ] TEE Logging enabled."); slog.log(E_DEBUG, "[ '" + task_name + "' ] DUP2: child_*_pipe[1]->STD*_FILENO"); @@ -292,16 +272,13 @@ int Sproc::execute(std::string shell, std::string environment_file, std::string close(child_stdout_pipe[WRITE_END]); close(child_stderr_pipe[WRITE_END]); + // buffers for reading from child fd's char stdout_buf[1000] = {0}; char stderr_buf[1000] = {0}; - // will contain a set of file descriptors to monitor representing stdout and stderr of the child process fd_set readfds; - - - // loop completion flags bool set_stdout_break = false; bool set_stderr_break = false; @@ -315,28 +292,24 @@ int Sproc::execute(std::string shell, std::string environment_file, std::string FD_SET( child_stdout_pipe[READ_END], & readfds ); FD_SET( child_stderr_pipe[READ_END], & readfds ); - + // for some reason select needs the highest number of the fd +1 of its own input int highest_fd = child_stderr_pipe[READ_END] > child_stdout_pipe[READ_END] ? child_stderr_pipe[READ_END] : child_stdout_pipe[READ_END]; + // wait for one of the fd's to become readable if ( select( highest_fd + 1, &readfds, NULL, NULL, NULL ) >= 0 ) { // can read any if ( FD_ISSET( child_stdout_pipe[READ_END], &readfds ) ) - { - // can read child stdout pipe - // so do so - + { // can read child stdout pipe // read and return the byte size of what was read int stdout_count = read(child_stdout_pipe[READ_END], stdout_buf, sizeof(stdout_buf) - 1); - // switch on the count size to allow for error return handling - switch(stdout_count) { + switch(stdout_count) { // switch on the count size to allow for error return handling case READ_PIPEOPEN_O_NONBLOCK: if ( errno == EINTR ) { continue; } else { - perror("read stdout"); - slog.log(E_FATAL, "PIPE ISSUE with STDOUT"); - exit(1); + slog.log(E_FATAL, "Pipe reading issue with child STDOUT."); + exit( PIPE_FAILED2 ); } case READ_EOF: // signal that STDOUT is complete @@ -345,6 +318,7 @@ int Sproc::execute(std::string shell, std::string environment_file, std::string default: tee_out.write( stdout_buf, stdout_count ); tee_out.flush(); + // clear the buffer to prevent artifacts from previous loop memset( &stdout_buf[0], 0, sizeof( stdout_buf ) -1 ); } @@ -363,19 +337,21 @@ int Sproc::execute(std::string shell, std::string environment_file, std::string continue; } else { perror( "read stderr" ); - slog.log( E_FATAL, "PIPE ISSUE WITH STDERR" ); - exit(1); + slog.log( E_FATAL, "Pipe reading issue with child STDERR." ); + exit( PIPE_FAILED3 ); } case READ_RESULTS::READ_EOF: + // let the loop know the STDERR criteria has been met set_stderr_break = true; - //break; + // continue the loop continue; default: + // write the buffer contents to the STDERR Tee tee_err.write( stderr_buf, stderr_count ); + // flush the TEE tee_err.flush(); // clear the buffer to prevent artifacts from previous loop memset( &stderr_buf[0], 0, sizeof( stderr_buf ) -1 ); - } } } else { @@ -384,9 +360,8 @@ int Sproc::execute(std::string shell, std::string environment_file, std::string } // end select/if } - - while ((pid = waitpid(pid, &exit_code_raw, 0)) == -1) {} - //waitpid( pid, &exit_code_raw, 0 ); + // wait for the child to exit + while ( ( pid = waitpid(pid, &exit_code_raw, 0 ) ) == -1 ) {} // clean up Tee stdout_log.close(); diff --git a/src/Sproc/Sproc.h b/src/Sproc/Sproc.h index 4f5adea..d178402 100644 --- a/src/Sproc/Sproc.h +++ b/src/Sproc/Sproc.h @@ -1,7 +1,7 @@ /* - Examplar - An automation and testing framework. + Rex - An automation and testing system. - © SURRO INDUSTRIES and Chris Punches, 2017. + SILO GROUP© and Chris Punches, 2017. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as @@ -15,30 +15,56 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . - */ #ifndef FTESTS_SPROC_H #define FTESTS_SPROC_H -#include +#include "../Logger/Logger.h" #include #include -#include "../Logger/Logger.h" +#include "errno.h" +#include +#include #include #include "unistd.h" +#include +#include +#include +#include +#include "fcntl.h" -// executes a subprocess and captures STDOUT, STDERR, and return code. -// should be able to recieve path of binary to be executed as well as any parameters -class Sproc { - public: - // call the object. returnvalue is enum representing external execution attempt not binary exit code - static int execute(std::string shell, std::string enviornment_file, std::string user_name, std::string group_name, std::string command, int LOG_LEVEL, std::string task_name ); +// exit codes for Rex +enum SPROC_RETURN_CODES { + SUCCESS = true, + UID_NOT_FOUND = -404, + GID_NOT_FOUND = -405, + SET_GID_FAILED = -401, + SET_UID_FAILED = -404, + EXEC_FAILURE_GENERAL = -666, + PIPE_FAILED3 = -996, + PIPE_FAILED2 = -997, + PIPE_FAILED = -998, + DUP2_FAILED = -999, + FORK_FAILED = -1000 }; +// executes a subprocess and captures STDOUT, STDERR, and return code. +// should be able to receive path of binary to be executed as well as any parameters +class Sproc { + public: + // call the object. return value is enum representing external execution attempt not binary exit code + static int execute( + std::string shell, + std::string environment_file, + std::string user_name, + std::string group_name, + std::string command, + int LOG_LEVEL, + std::string task_name + ); +}; -// mostly lifted from: -// http://wordaligned.org/articles/cpp-streambufs // a teebuff implementation class teebuf: public std::streambuf { @@ -63,4 +89,3 @@ class teestream : public std::ostream }; #endif //FTESTS_SPROC_H -