early rex, finally

master
Chris Punches 2021-03-25 02:11:23 -04:00
parent 31ed2feb7f
commit dce543a15d
4 changed files with 116 additions and 51 deletions

View File

@ -8,6 +8,7 @@
#include <cstring> #include <cstring>
#include "fcntl.h" #include "fcntl.h"
#define PARENT default
// converts username to UID // converts username to UID
// returns false on failure // returns false on failure
@ -77,8 +78,13 @@ teestream::teestream(std::ostream &o1, std::ostream &o2) : std::ostream( &tbuf),
{} {}
enum PIPE_FILE_DESCRIPTORS { enum PIPE_FILE_DESCRIPTORS {
READ_FD = 0, READ_END = 0,
WRITE_FD = 1 WRITE_END = 1
};
enum READ_RESULTS {
READ_EOF = 0,
READ_PIPEOPEN_O_NONBLOCK = -1
}; };
@ -185,19 +191,23 @@ 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 ); 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 ); stderr_log.open( child_stderr_log_path.c_str(), std::ofstream::out | std::ofstream::app );
// avoid cyclic dependencies between stdout and tee_out // avoid cyclic dependencies between stdout and tee_out
std::ostream tmp_stdout( std::cout.rdbuf() ); std::ostream tmp_stdout( std::cout.rdbuf() );
std::ostream tmp_stderr( std::cerr.rdbuf() ); std::ostream tmp_stderr( std::cerr.rdbuf() );
// writing to this ostream derivative will write to stdout log file and std::cout // writing to this ostream derivative will write to stdout log file and std::cout
teestream tee_out(tmp_stdout, stdout_log); teestream tee_out(tmp_stdout, stdout_log);
teestream tee_err(tmp_stderr, stderr_log); teestream tee_err(tmp_stderr, stderr_log);
// pop the cout/cerr buffers to the appropriate Tees' buffers // pop the cout/cerr buffers to the appropriate Tees' buffers
std::cout.rdbuf( tee_out.rdbuf() );
std::cerr.rdbuf( tee_err.rdbuf() ); // These cause a segfault when used with the I/O redirection happening around fork, pipe, dup2, execl...
// end - "set up the 'Tee' with the parent" //std::cout.rdbuf( tee_out.rdbuf() );
slog.log( E_INFO, "Tee Logging enabled for \"" + task_name + "\""); //std::cerr.rdbuf( tee_err.rdbuf() );
// ....and I don't know why.
// build the command to execute in the shell // build the command to execute in the shell
@ -206,29 +216,31 @@ int Sproc::execute(std::string shell, std::string environment_file, std::string
slog.log(E_DEBUG, "[ '" + task_name + "' ] Shell call for loading: ``" + sourcer + "``."); slog.log(E_DEBUG, "[ '" + task_name + "' ] Shell call for loading: ``" + sourcer + "``.");
// file descriptors for parent/child i/o // file descriptors for parent/child i/o
int stdout_filedes[2]; int child_stdout_pipe[2];
int child_stderr_pipe[2];
slog.log( E_DEBUG, "[ '" + task_name + "' ] STDIN/STDOUT/STDERR file descriptors created." ); slog.log( E_DEBUG, "[ '" + task_name + "' ] STDIN/STDOUT/STDERR file descriptors created." );
// man 3 pipe // man 3 pipe
if (pipe(stdout_filedes) == -1 ) { if (pipe(child_stdout_pipe) == -1 ) {
slog.log(E_FATAL, "[ '" + task_name + "' ] PIPE FAILED"); slog.log(E_FATAL, "[ '" + task_name + "' ] STDOUT PIPE FAILED");
return SPROC_RETURN_CODES::PIPE_FAILED; return SPROC_RETURN_CODES::PIPE_FAILED;
} else { } else {
slog.log(E_DEBUG, "[ '" + task_name + "' ] file descriptors piped."); slog.log(E_DEBUG, "[ '" + task_name + "' ] file descriptors piped.");
} }
// man 3 pipe
// // avoids the need to take any explicit action within the child process to close file descriptors if (pipe(child_stderr_pipe) == -1 ) {
// if (fcntl(stdout_filedes[READ_FD], F_SETFD, FD_CLOEXEC) == -1) { slog.log(E_FATAL, "[ '" + task_name + "' ] STDERR PIPE FAILED");
// perror("fcntl"); return SPROC_RETURN_CODES::PIPE_FAILED;
// exit(1); } else {
// } slog.log(E_DEBUG, "[ '" + task_name + "' ] file descriptors piped.");
}
// fork a process // fork a process
pid_t pid = fork(); pid_t pid = fork();
slog.log( E_DEBUG, "[ '" + task_name + "' ] Process forked. Reporting. (PID: " + std::to_string(pid) + ")" ); slog.log( E_DEBUG, "[ '" + task_name + "' ] Process forked. Reporting. (PID: " + std::to_string(pid) + ")" );
switch ( pid ) { switch ( pid ) {
case FORK_STATES::FORK_FAILURE: case FORK_STATES::FORK_FAILURE:
{ {
@ -241,22 +253,30 @@ int Sproc::execute(std::string shell, std::string environment_file, std::string
// enter child process // enter child process
slog.log(E_DEBUG, "[ '" + task_name + "' ] Entering child process."); slog.log(E_DEBUG, "[ '" + task_name + "' ] Entering child process.");
while ((dup2(stdout_filedes[WRITE_FD], STDOUT_FILENO) == -1) && (errno == EINTR)) {} while ((dup2(child_stdout_pipe[WRITE_END], STDOUT_FILENO) == -1) && (errno == EINTR)) {}
close( stdout_filedes[WRITE_FD] ); while ((dup2(child_stderr_pipe[WRITE_END], STDERR_FILENO) == -1) && (errno == EINTR)) {}
close( stdout_filedes[READ_FD] );
slog.log(E_DEBUG, "[ '" + task_name + "' ] DUP2 on stdout_filedes[1]->STDOUT_FILENO in child.");
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");
// set identity context // set identity context
// set gid and uid // set gid and uid
int context_status = set_identity_context(task_name, user_name, group_name, slog); int context_status = set_identity_context(task_name, user_name, group_name, slog);
if (!(context_status)) { if (!(context_status)) {
slog.log(E_FATAL, "[ '" + task_name + "' ] Identity context switch failed."); slog.log(E_FATAL, "[ '" + task_name + "' ] Identity context set failed.");
return context_status; return context_status;
} else {
slog.log( E_INFO, "[ '" + task_name + "' ] Identity context set as user '" + user_name + "' and group '" + group_name + "'." );
} }
// execute our big nasty thing
// exit_code_raw = system( sourcer.c_str() );
int ret = execl("/bin/sh", "/bin/sh", "-c", sourcer.c_str(), (char *) NULL); int ret = execl("/bin/sh", "/bin/sh", "-c", sourcer.c_str(), (char *) NULL);
// print something useful to debug with if execl fails // print something useful to debug with if execl fails
@ -266,45 +286,76 @@ int Sproc::execute(std::string shell, std::string environment_file, std::string
exit(exit_code_raw); exit(exit_code_raw);
} }
default: PARENT:
{ {
// parent process // enter the parent process
close(stdout_filedes[WRITE_FD]); close(child_stdout_pipe[WRITE_END]);
// --- close(child_stderr_pipe[WRITE_END]);
// clean up Tee
stdout_log.close();
stderr_log.close();
char stdout_buf[1000] = {0};
char buffer[1000] = {0}; char stderr_buf[1000] = {0};
std::cout.flush(); std::cout.flush();
std::cerr.flush();
bool set_break = false;
// read from fd until child completes // read from fd until child completes
while ( 1 ) { while (! set_break ) {
ssize_t count = read(stdout_filedes[READ_FD], buffer, sizeof(buffer)); ssize_t stdout_count = read(child_stdout_pipe[READ_END], stdout_buf, sizeof(stdout_buf) - 1);
if (count == -1) {
if (errno == EINTR) { // cycle through STDOUT
continue; switch (stdout_count) {
} else { case READ_PIPEOPEN_O_NONBLOCK:
perror("read"); if (errno == EINTR) {
exit(1); continue;
} } else {
} else if (count == 0) { perror("read");
break; slog.log(E_FATAL, "PIPE ISSUE with STDOUT");
} else { exit(1);
std::cout << buffer; }
std::cout.flush(); case READ_EOF:
memset(&buffer[0], 0, sizeof(buffer)); set_break = true;
// handle_child_process_output(buffer, count); break;
default:
tee_out.write(stdout_buf, stdout_count);
tee_out.flush();
memset(&stdout_buf[0], 0, sizeof(stdout_buf));
// END SWITCH
} }
} }
set_break = false;
while(! set_break ) {
ssize_t stderr_count = read(child_stderr_pipe[READ_END], stderr_buf, sizeof(stderr_buf) - 1 );
switch ( stderr_count )
{
case READ_PIPEOPEN_O_NONBLOCK:
if (errno == EINTR) {
continue;
} else {
perror("read");
slog.log( E_FATAL, "PIPE ISSUE with STDERR" );
exit(1);
}
case READ_EOF:
set_break = true;
break;
default:
tee_err.write( stderr_buf, stderr_count );
tee_err.flush();
memset(&stderr_buf[0], 0, sizeof(stderr_buf));
// END SWITCH
}
}
while ((pid = waitpid(pid, &exit_code_raw, 0)) == -1) {} while ((pid = waitpid(pid, &exit_code_raw, 0)) == -1) {}
//waitpid( pid, &exit_code_raw, 0 ); //waitpid( pid, &exit_code_raw, 0 );
// clean up Tee
stdout_log.close();
stderr_log.close();
} }
} }
return WEXITSTATUS( exit_code_raw ); return WEXITSTATUS( exit_code_raw );
} }

View File

@ -1,2 +1,3 @@
echo "independent test 2 output" echo "independent test 2 output"
echo "independent test says TEST_VAR is ${TEST_VAR}"
exit $? exit $?

View File

@ -3,6 +3,7 @@
{ "name": "independent test 1", "dependencies": [ null ] }, { "name": "independent test 1", "dependencies": [ null ] },
{ "name": "independent test 2", "dependencies": [ null ] }, { "name": "independent test 2", "dependencies": [ null ] },
{ "name": "dependent test", "dependencies": [ "independent test 1" ] }, { "name": "dependent test", "dependencies": [ "independent test 1" ] },
{ "name": "curses dialog", "dependencies": [ null ] } { "name": "curses dialog", "dependencies": [ null ] },
{ "name": "fail", "dependencies": [ null ] }
] ]
} }

View File

@ -47,6 +47,18 @@
"rectify": false, "rectify": false,
"shell": "/usr/bin/env bash", "shell": "/usr/bin/env bash",
"environment": "environments/examplar.variables" "environment": "environments/examplar.variables"
},
{
"name": "fail",
"target": "components/fail.bash",
"rectifier": "",
"active": true,
"required": false,
"user": "bagira",
"group": "bagira",
"rectify": false,
"shell": "/usr/bin/env bash",
"environment": "environments/examplar.variables"
} }
] ]
} }