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 "fcntl.h"
#define PARENT default
// converts username to UID
// returns false on failure
@ -77,8 +78,13 @@ teestream::teestream(std::ostream &o1, std::ostream &o2) : std::ostream( &tbuf),
{}
enum PIPE_FILE_DESCRIPTORS {
READ_FD = 0,
WRITE_FD = 1
READ_END = 0,
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 );
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
std::cout.rdbuf( tee_out.rdbuf() );
std::cerr.rdbuf( tee_err.rdbuf() );
// end - "set up the 'Tee' with the parent"
slog.log( E_INFO, "Tee Logging enabled for \"" + task_name + "\"");
// These cause a segfault when used with the I/O redirection happening around fork, pipe, dup2, execl...
//std::cout.rdbuf( tee_out.rdbuf() );
//std::cerr.rdbuf( tee_err.rdbuf() );
// ....and I don't know why.
// 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 + "``.");
// 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." );
// man 3 pipe
if (pipe(stdout_filedes) == -1 ) {
slog.log(E_FATAL, "[ '" + task_name + "' ] PIPE FAILED");
if (pipe(child_stdout_pipe) == -1 ) {
slog.log(E_FATAL, "[ '" + task_name + "' ] STDOUT PIPE FAILED");
return SPROC_RETURN_CODES::PIPE_FAILED;
} else {
slog.log(E_DEBUG, "[ '" + task_name + "' ] file descriptors piped.");
}
// // avoids the need to take any explicit action within the child process to close file descriptors
// if (fcntl(stdout_filedes[READ_FD], F_SETFD, FD_CLOEXEC) == -1) {
// perror("fcntl");
// exit(1);
// }
// man 3 pipe
if (pipe(child_stderr_pipe) == -1 ) {
slog.log(E_FATAL, "[ '" + task_name + "' ] STDERR PIPE FAILED");
return SPROC_RETURN_CODES::PIPE_FAILED;
} else {
slog.log(E_DEBUG, "[ '" + task_name + "' ] file descriptors piped.");
}
// fork a process
pid_t pid = fork();
slog.log( E_DEBUG, "[ '" + task_name + "' ] Process forked. Reporting. (PID: " + std::to_string(pid) + ")" );
switch ( pid ) {
case FORK_STATES::FORK_FAILURE:
{
@ -241,22 +253,30 @@ int Sproc::execute(std::string shell, std::string environment_file, std::string
// enter child process
slog.log(E_DEBUG, "[ '" + task_name + "' ] Entering child process.");
while ((dup2(stdout_filedes[WRITE_FD], STDOUT_FILENO) == -1) && (errno == EINTR)) {}
close( stdout_filedes[WRITE_FD] );
close( stdout_filedes[READ_FD] );
slog.log(E_DEBUG, "[ '" + task_name + "' ] DUP2 on stdout_filedes[1]->STDOUT_FILENO in child.");
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] );
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 gid and uid
int context_status = set_identity_context(task_name, user_name, group_name, slog);
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;
} else {
slog.log( E_INFO, "[ '" + task_name + "' ] Identity context set as user '" + user_name + "' and group '" + group_name + "'." );
}
// exit_code_raw = system( sourcer.c_str() );
// execute our big nasty thing
int ret = execl("/bin/sh", "/bin/sh", "-c", sourcer.c_str(), (char *) NULL);
// 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);
}
default:
PARENT:
{
// parent process
close(stdout_filedes[WRITE_FD]);
// ---
// clean up Tee
stdout_log.close();
stderr_log.close();
// enter the parent process
close(child_stdout_pipe[WRITE_END]);
close(child_stderr_pipe[WRITE_END]);
char buffer[1000] = {0};
char stdout_buf[1000] = {0};
char stderr_buf[1000] = {0};
std::cout.flush();
std::cerr.flush();
bool set_break = false;
// read from fd until child completes
while ( 1 ) {
ssize_t count = read(stdout_filedes[READ_FD], buffer, sizeof(buffer));
if (count == -1) {
while (! set_break ) {
ssize_t stdout_count = read(child_stdout_pipe[READ_END], stdout_buf, sizeof(stdout_buf) - 1);
// cycle through STDOUT
switch (stdout_count) {
case READ_PIPEOPEN_O_NONBLOCK:
if (errno == EINTR) {
continue;
} else {
perror("read");
slog.log(E_FATAL, "PIPE ISSUE with STDOUT");
exit(1);
}
} else if (count == 0) {
case READ_EOF:
set_break = true;
break;
} else {
std::cout << buffer;
std::cout.flush();
memset(&buffer[0], 0, sizeof(buffer));
// handle_child_process_output(buffer, count);
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) {}
//waitpid( pid, &exit_code_raw, 0 );
// clean up Tee
stdout_log.close();
stderr_log.close();
}
}
}
}
return WEXITSTATUS( exit_code_raw );
}

View File

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

View File

@ -3,6 +3,7 @@
{ "name": "independent test 1", "dependencies": [ null ] },
{ "name": "independent test 2", "dependencies": [ null ] },
{ "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,
"shell": "/usr/bin/env bash",
"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"
}
]
}