diff --git a/conf/units/all_test.units b/conf/units/all_test.units index 5e98e87..92a4170 100644 --- a/conf/units/all_test.units +++ b/conf/units/all_test.units @@ -2,35 +2,35 @@ "units": [ { "name": "independent test 1", - "target": "(>&2 echo 'error is a doodle day')", - "rectifier": "/usr/bin/true", - "active": true, - "required": true, - "rectify": true - }, - { - "name": "independent test 2", "target": "/usr/bin/false", "rectifier": "/usr/bin/true", "active": true, "required": true, "rectify": false }, + { + "name": "independent test 2", + "target": "/usr/bin/false", + "rectifier": "/usr/bin/false", + "active": true, + "required": false, + "rectify": false + }, { "name": "A DEFINITION THAT IS NOT USED", "target": "/usr/bin/false", - "rectifier": "/usr/bin/true", + "rectifier": "/usr/bin/false", "active": true, "required": true, - "rectify": false + "rectify": true }, { "name": "dependent test", "target": "/usr/bin/false", - "rectifier": "/usr/bin/true", + "rectifier": "/usr/bin/false", "active": true, "required": true, - "rectify": false + "rectify": true } ] } diff --git a/src/loaders/Task.cpp b/src/loaders/Task.cpp index 889f37d..e3ebe17 100644 --- a/src/loaders/Task.cpp +++ b/src/loaders/Task.cpp @@ -13,6 +13,15 @@ class Task_NotReady: public std::runtime_error { public: Task_NotReady(): std::runtime_error("Task: Attempted to access a unit of a Task that is not defined.") {} }; +/// Task_RequiredButFailedTask - Exception thrown when a Task is failed but required, and rectification also failed. +class Task_RequiredButFailedTask: public std::runtime_error { public: + Task_RequiredButFailedTask(): std::runtime_error("Task: Attempted to access a unit of a Task that failed, but was required, and the corresponding rectification target also failed..") {} +}; + +/// Task_RequiredButFailedTask - Exception thrown when a Task is failed but required, and rectification also failed but returned with a zero exit code (dont try to fool the check). +class Task_RequiredButRectifierDoesNotHeal: public std::runtime_error { public: + Task_RequiredButRectifierDoesNotHeal(): std::runtime_error("Task: The rectification script was executed and reported success, but did not actually heal the faulty condition of the Task target.") {} +}; /// Task::Task() - Constructor for the Task class. The Task is the building block of a Plan indicating of which Unit to /// execute, and its dependencies on other units to have already been completed successfully. @@ -86,33 +95,130 @@ bool Task::has_definition() { } /// Task::execute - execute a task's unit definition. +/// See the design document for what flow control needs to look like here. +/// \param verbose - Verbosity level - not implemented yet. void Task::execute( bool verbose ) { - // throw if unit not coupled + // DUFFING + + // PREWORK + // throw if unit not coupled to all necessary values since Task is stateful (stateful is okay) if (! this->has_definition() ) { throw Task_NotReady(); } + // get the name + std::string task_name = this->definition.get_name(); + // END PREWORK + + + // get the target execution command + std::string target_command = this->definition.get_target(); + + // if we're in verbose mode, do some verbose things if ( verbose ) { - std::cout << "\t Using unit \"" << this->definition.get_name() << "\"." << std::endl; - std::cout << "\t Executing target \"" << this->definition.get_target() << "\"." << std::endl; + std::cout << "\tUsing unit \"" << task_name << "\"." << std::endl; + std::cout << "\tExecuting target \"" << target_command << "\"." << std::endl; } - std::string executionString = this->definition.get_target(); - std::string rectifierString = this->definition.get_rectifier(); + // execute target + int return_code = Sproc::execute( target_command ); - int return_code = Sproc::execute( executionString ); - - if ( return_code ) + // d[0] check exit code of target + if (return_code == 0) { - std::cout << "Process failed with exit code " << return_code << "." << std::endl; - - std::cout << "Performing rectification: " << rectifierString << "." << std::endl; - int rectifier_error = Sproc::execute( rectifierString ); - - if ( rectifier_error ) + // Zero d[0] return from target execution, good to return + if ( verbose ) { - std::cout << "Designated rectification script failed with error " << rectifier_error << "." << std::endl; + std::cout << "\tTarget " << task_name << " succeeded." << std::endl; } + // next + return; + } else { + // Non-Zero d[0] from initial target execution, get to d[1] + std::cout << "\tTarget \"" << task_name << "\" failed with exit code " << return_code << "." << std::endl; + + // check if rectify pattern is enabled d[1] + if ( this->definition.get_rectify() ) + { + // yes d[1] + std::cout << "\tRectification pattern is enabled for \"" << task_name << "\"." << std::endl; + // execute RECTIFIER + std::string rectifier_command = this->definition.get_rectifier(); + std::cout << "\tExecuting rectification: " << rectifier_command << "." << std::endl; + int rectifier_error = Sproc::execute( rectifier_command ); + + // d[3] check exit code of rectifier + if ( rectifier_error ) + { + //d[3] non-zero + + std::cout << "\tRectification of \"" << task_name << "\" failed with exit code " << rectifier_error << "." << std::endl; + // d[2] check if REQUIRED + if ( this->definition.get_required() ) + { + // d[2] yes + // halt/exception + throw Task_RequiredButFailedTask(); + } else { + // d[2] no + // next + return; + } + } else { + // d[3] zero + + // execute target + std::cout << "\tRe-Executing target \"" << this->definition.get_target() << "\"." << std::endl; + int retry_code = Sproc::execute( target_command ); + + // d[4] exit code of target retry + if (retry_code == 0) { + // d[4] zero + return; + } else { + // d[4] non-zero + // d[5] required check + if ( this->definition.get_required() ) + { + // d[5] yes + throw Task_RequiredButRectifierDoesNotHeal(); + } else { + // d[5] no + // next + return; + } + } + } + } else { + // no d[1] + std::cout << "\tRectification is not enabled for \"" << task_name << "\"." << std::endl; + // required d[2] + if ( this->definition.get_required() ) + { + // d[2] yes + // This is executing..... + std::cout << "\tThis task is required to continue the plan." << std::endl; + // but these are NOT executing????? + throw Task_RequiredButFailedTask(); + throw Task_RequiredButFailedTask(); + throw Task_RequiredButFailedTask(); + throw Task_RequiredButFailedTask(); + throw Task_RequiredButFailedTask(); + throw Task_RequiredButFailedTask(); + throw Task_RequiredButFailedTask(); + throw Task_RequiredButFailedTask(); + throw Task_RequiredButFailedTask(); + throw Task_RequiredButFailedTask(); + throw Task_RequiredButFailedTask(); + throw Task_RequiredButFailedTask(); + throw Task_RequiredButFailedTask(); + throw Task_RequiredButFailedTask(); + } else { + // d[2] no + std::cout << "\tThis task is not required to continue the plan." << std::endl; + return; + } + } } } \ No newline at end of file diff --git a/src/loaders/Unit.cpp b/src/loaders/Unit.cpp index 23f71e6..cec831b 100644 --- a/src/loaders/Unit.cpp +++ b/src/loaders/Unit.cpp @@ -52,13 +52,13 @@ int Unit::load_root(Json::Value loader_root) { this->rectifier = loader_root.get("rectifier", errmsg).asString(); } else throw Unit_DataStructureException(); if ( loader_root.isMember("active") ) - { this->active = loader_root.get("active", errmsg).asString(); } else throw Unit_DataStructureException(); + { this->active = loader_root.get("active", errmsg).asBool(); } else throw Unit_DataStructureException(); if ( loader_root.isMember("required") ) - { this->required = loader_root.get("required", errmsg).asString(); } else throw Unit_DataStructureException(); + { this->required = loader_root.get("required", errmsg).asBool(); } else throw Unit_DataStructureException(); if ( loader_root.isMember("rectify") ) - { this->rectify = loader_root.get("rectify", errmsg).asString(); } else throw Unit_DataStructureException(); + { this->rectify = loader_root.get("rectify", errmsg).asBool(); } else throw Unit_DataStructureException(); this->populated = true; @@ -119,7 +119,7 @@ std::string Unit::get_rectifier() /// Unit::get_active - retrieves the armed status of the unit. /// /// \return the armed status of the unit. -std::string Unit::get_active() +bool Unit::get_active() { if ( ! this->populated ) { throw Unit_NotPopulated(); } return this->active; @@ -128,7 +128,7 @@ std::string Unit::get_active() /// Unit::get_required - retrieves the requirement status of the unit. /// /// \return the requirement status of the unit. -std::string Unit::get_required() +bool Unit::get_required() { if ( ! this->populated ) { throw Unit_NotPopulated(); } return this->required; @@ -137,7 +137,7 @@ std::string Unit::get_required() /// Unit::get_rectify - retrieves the rectification status of the unit. /// /// \return the rectification status of the unit. -std::string Unit::get_rectify() +bool Unit::get_rectify() { if ( ! this->populated ) { throw Unit_NotPopulated(); } return this->rectify; diff --git a/src/loaders/Unit.h b/src/loaders/Unit.h index 9c2247d..9d6366d 100644 --- a/src/loaders/Unit.h +++ b/src/loaders/Unit.h @@ -28,15 +28,15 @@ private: // an indicator of whether the test is active or not // this is used as a way to give definers a way to force executors to edit arbitrary fields or prevent // execution of potentially dangerous or intrusive tests - std::string active; + bool active; // an indicator of whether or not this test is required to pass. // intended to be used as a flag to halt execution of further tests on failure - std::string required; + bool required; // indicator of whether the rectifier executable should be run on test failures. // if rectifier exits on non-zero return code, it should be trigger the behaviour indicated by required - std::string rectify; + bool rectify; public: Unit(); @@ -52,9 +52,9 @@ public: std::string get_target(); std::string get_output(); std::string get_rectifier(); - std::string get_active(); - std::string get_required(); - std::string get_rectify(); + bool get_active(); + bool get_required(); + bool get_rectify(); }; #endif //FTESTS_UNIT_H diff --git a/src/sproc/Sproc.cpp b/src/sproc/Sproc.cpp index d3dcfc9..882721a 100644 --- a/src/sproc/Sproc.cpp +++ b/src/sproc/Sproc.cpp @@ -7,34 +7,23 @@ /// \param input - The commandline input to execute. /// \return - The return code of the execution of input in the calling shell. int Sproc::execute(std::string input) { - std::cout << std::endl << "made it to subprocess execution but not implemented yet" << std::endl << std::endl; - int PIPE_READ = 0; int PIPE_WRITE = 1; int stdin_pipe[2]; - // int aStderrPipe[2]; + int stderr_pipe[2]; int stdout_pipe[2]; int child_pid; char nChar; int child_exit_code; - if (pipe(stdin_pipe) < 0) + if ( pipe(stdin_pipe) < 0 ) { perror("allocating pipe for child input redirect"); return -1; } -/* - if (pipe(aStderrPipe < 0)) { - close(aStderrPipe[PIPE_READ]); - close(aStderrPipe[PIPE_WRITE]); - perror("allocating pipe for error redirect"); - return -1; - } -*/ - - if (pipe(stdout_pipe) < 0) + if ( pipe(stdout_pipe) < 0 ) { close(stdin_pipe[PIPE_READ]); close(stdin_pipe[PIPE_WRITE]); @@ -42,6 +31,14 @@ int Sproc::execute(std::string input) { return -1; } + if ( pipe(stderr_pipe) < 0 ) { + close(stderr_pipe[PIPE_READ]); + close(stderr_pipe[PIPE_WRITE]); + perror("allocating pipe for error redirect"); + return -1; + } + + child_pid = fork(); if (0 == child_pid) { @@ -58,41 +55,41 @@ int Sproc::execute(std::string input) { } // redirect stderr - if (dup2(stdout_pipe[PIPE_WRITE], STDERR_FILENO) == -1) { + if (dup2(stderr_pipe[PIPE_WRITE], STDERR_FILENO) == -1) { exit(errno); } // all these are for use by parent only close(stdin_pipe[PIPE_READ]); close(stdin_pipe[PIPE_WRITE]); + close(stdout_pipe[PIPE_READ]); close(stdout_pipe[PIPE_WRITE]); + close(stderr_pipe[PIPE_READ]); + close(stderr_pipe[PIPE_WRITE]); + + // run child process image // replace this with any exec* function find easier to use ("man exec") child_exit_code = system( input.c_str() ); + // if we get here at all, an error occurred, but we are in the child // process, so just exit - exit(child_exit_code); + return WEXITSTATUS(child_exit_code); } else if (child_pid > 0) { // parent continues here // close unused file descriptors, these are for child only close(stdin_pipe[PIPE_READ]); close(stdout_pipe[PIPE_WRITE]); - - // da fuq? -/* // Include error check here - if (NULL != szMessage) - { - write(stdin_pipe[PIPE_WRITE], szMessage, strlen(szMessage)); - } -*/ + close(stderr_pipe[PIPE_WRITE]); // Just a char by char read here, you can change it accordingly - while (read(stdout_pipe[PIPE_READ], &nChar, 1) == 1) + while ( read( stdout_pipe[PIPE_READ], &nChar, 1 ) == 1 ) { + // does this loop also need to involve STDERR? -CP write(STDOUT_FILENO, &nChar, 1); } @@ -100,12 +97,18 @@ int Sproc::execute(std::string input) { // open of course as long as you want to talk to the child close(stdin_pipe[PIPE_WRITE]); close(stdout_pipe[PIPE_READ]); + close(stderr_pipe[PIPE_READ]); } else { // failed to create child close(stdin_pipe[PIPE_READ]); close(stdin_pipe[PIPE_WRITE]); + close(stdout_pipe[PIPE_READ]); close(stdout_pipe[PIPE_WRITE]); + + close(stderr_pipe[PIPE_READ]); + close(stderr_pipe[PIPE_WRITE]); } - return child_exit_code; + + return WEXITSTATUS(child_exit_code); } \ No newline at end of file