From 5713e6f88b48c7cd6e7df63005fc77d4550fb733 Mon Sep 17 00:00:00 2001 From: ODNADev <32846534+Anilkumar18@users.noreply.github.com> Date: Mon, 4 Aug 2025 13:44:56 +0530 Subject: [PATCH] lcpex enhancement - initial commit --- CMakeLists.txt | 3 +- src/lcpex/FileHandle.h | 54 +++++ src/lcpex/liblcpex.cpp | 215 +++++++++++++++---- src/lcpex/vpty/libclpex_tty.cpp | 245 ++++++++++++++++----- src/logger/Logger.cpp | 152 ++++++++++++- src/logger/Logger.h | 33 ++- tatus | 370 ++++++++++++++++++++++++++++++++ 7 files changed, 969 insertions(+), 103 deletions(-) create mode 100644 src/lcpex/FileHandle.h create mode 100644 tatus diff --git a/CMakeLists.txt b/CMakeLists.txt index b5531e1..ad3cea8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,4 +3,5 @@ project(rex) set(CMAKE_CXX_STANDARD 14) -add_executable(rex Rex.cpp src/json_support/jsoncpp/json.h src/json_support/jsoncpp/json-forwards.h src/json_support/jsoncpp/jsoncpp.cpp src/logger/Logger.cpp src/logger/Logger.h src/json_support/JSON.cpp src/json_support/JSON.h src/misc/helpers.cpp src/misc/helpers.h src/config/Config.cpp src/config/Config.h src/suite/Suite.cpp src/suite/Suite.h src/suite/Unit.cpp src/suite/Unit.h src/shells/shells.cpp src/shells/shells.h src/plan/Plan.cpp src/plan/Plan.h src/plan/Task.cpp src/plan/Task.h src/lcpex/helpers.h src/lcpex/helpers.cpp src/lcpex/liblcpex.h src/lcpex/liblcpex.cpp src/lcpex/vpty/libclpex_tty.h src/lcpex/vpty/libclpex_tty.cpp src/lcpex/Contexts.h src/lcpex/Contexts.cpp src/lcpex/helpers.h src/lcpex/string_expansion/string_expansion.h src/lcpex/string_expansion/string_expansion.cpp src/lcpex/vpty/pty_fork_mod/pty_fork.h src/lcpex/vpty/pty_fork_mod/pty_fork.cpp src/lcpex/vpty/pty_fork_mod/pty_master_open.h src/lcpex/vpty/pty_fork_mod/pty_master_open.cpp src/lcpex/vpty/pty_fork_mod/tty_functions.h src/lcpex/vpty/pty_fork_mod/tty_functions.cpp ) +add_executable(rex Rex.cpp src/json_support/jsoncpp/json.h src/json_support/jsoncpp/json-forwards.h src/json_support/jsoncpp/jsoncpp.cpp src/logger/Logger.cpp src/logger/Logger.h src/json_support/JSON.cpp src/json_support/JSON.h src/misc/helpers.cpp src/misc/helpers.h src/config/Config.cpp src/config/Config.h src/suite/Suite.cpp src/suite/Suite.h src/suite/Unit.cpp src/suite/Unit.h src/shells/shells.cpp src/shells/shells.h src/plan/Plan.cpp src/plan/Plan.h src/plan/Task.cpp src/plan/Task.h src/lcpex/helpers.h src/lcpex/helpers.cpp src/lcpex/liblcpex.h src/lcpex/liblcpex.cpp src/lcpex/vpty/libclpex_tty.h src/lcpex/vpty/libclpex_tty.cpp src/lcpex/Contexts.h src/lcpex/Contexts.cpp src/lcpex/helpers.h src/lcpex/string_expansion/string_expansion.h src/lcpex/string_expansion/string_expansion.cpp src/lcpex/vpty/pty_fork_mod/pty_fork.h src/lcpex/vpty/pty_fork_mod/pty_fork.cpp src/lcpex/vpty/pty_fork_mod/pty_master_open.h src/lcpex/vpty/pty_fork_mod/pty_master_open.cpp src/lcpex/vpty/pty_fork_mod/tty_functions.h src/lcpex/vpty/pty_fork_mod/tty_functions.cpp + src/lcpex/FileHandle.h) diff --git a/src/lcpex/FileHandle.h b/src/lcpex/FileHandle.h new file mode 100644 index 0000000..96ba22c --- /dev/null +++ b/src/lcpex/FileHandle.h @@ -0,0 +1,54 @@ +#pragma once +#include // for close() + +// Simplified RAII wrapper for file descriptors (C-style compatible) +class FdGuard { + int fd_; +public: + // Constructor + explicit FdGuard(int fd = -1) : fd_(fd) {} + + // Disable copy semantics + FdGuard(const FdGuard&) = delete; + FdGuard& operator=(const FdGuard&) = delete; + + // Move constructor + FdGuard(FdGuard&& other) /* no noexcept */ { + fd_ = other.fd_; + other.fd_ = -1; + } + + // Move assignment + FdGuard& operator=(FdGuard&& other) /* no noexcept */ { + if (this != &other) { + reset(); // Close current if valid + fd_ = other.fd_; + other.fd_ = -1; + } + return *this; + } + + // Destructor + ~FdGuard() { + close_if_valid(); + } + + // Reset with new fd (or close if -1) + void reset(int new_fd = -1) { + close_if_valid(); + fd_ = new_fd; + } + + // Get the raw fd + int get() const { + return fd_; + } + +private: + void close_if_valid() { + if (fd_ != -1) { + ::close(fd_); + fd_ = -1; + } + } +}; diff --git a/src/lcpex/liblcpex.cpp b/src/lcpex/liblcpex.cpp index b0e033e..db79c19 100644 --- a/src/lcpex/liblcpex.cpp +++ b/src/lcpex/liblcpex.cpp @@ -1,6 +1,70 @@ #include "liblcpex.h" +#include +#include +#include +#include +#include "../logger/Logger.h" +#include "FileHandle.h" +#define BUFFER_SIZE 1024 +// Initialize logger +static Logger logger(E_INFO, "lcpex"); +std::string user = logger.get_user_name(); +std::string group = logger.get_group_name(); + +void signal_handler(int sig) { + std::string command = "signal_handler"; // Command context + + // Create an instance of Logger (you can change the log level and mask accordingly) + // Log the signal handling + if (sig == SIGCHLD) { + // Log that SIGCHLD was received, but leave the exit status to the parent + logger.log_to_json_file("E_INFO", "SIGCHLD received. A child process ended.", user, group, command); + } else if (sig == SIGINT) { + logger.log_to_json_file("E_FATAL", "SIGINT received. Gracefully terminating...", user, group, command); + } else if (sig == SIGTERM) { + logger.log_to_json_file("E_FATAL", "SIGTERM received. Terminating...", user, group, command); + } else if (sig == SIGSEGV) { + logger.log_to_json_file("E_FATAL", "SIGSEGV received. Possible segmentation fault.", user, group, command); + } else { + logger.log_to_json_file("E_FATAL", "Unhandled signal received", user, group, command); + } +} + +// Setup signal registrations +void setup_signal_handlers() { + struct sigaction sa; + sa.sa_handler = signal_handler; // <-- handler function + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_RESTART | SA_NOCLDSTOP; + std::string command = "setup_signal_handlers"; // Command context + + // SIGCHLD + if (sigaction(SIGCHLD, &sa, nullptr) == -1) { + std::string error_message = "Failed to set SIGCHLD handler: " + std::string(strerror(errno)); + logger.log_to_json_file("E_FATAL", error_message, user, group, command); // Log to JSON file + } + + // SIGINT + if (sigaction(SIGINT, &sa, nullptr) == -1) { + std::string error_message = "Failed to set SIGINT handler: " + std::string(strerror(errno)); + logger.log_to_json_file("E_FATAL", error_message, user, group, command); // Log to JSON file + } + + // SIGTERM + if (sigaction(SIGTERM, &sa, nullptr) == -1) { + std::string error_message = "Failed to set SIGTERM handler: " + std::string(strerror(errno)); + logger.log_to_json_file("E_FATAL", error_message, user, group, command); // Log to JSON file + } + + // SIGSEGV + if (sigaction(SIGSEGV, &sa, nullptr) == -1) { + std::string error_message = "Failed to set SIGSEGV handler: " + std::string(strerror(errno)); + logger.log_to_json_file("E_FATAL", error_message, user, group, command); // Log to JSON file + } +} + std::string prefix_generator( std::string command, bool is_shell_command, @@ -37,8 +101,12 @@ std::string prefix_generator( // it's not a shell command, so we can just execute it directly prefix = command; } - std::cout << "LAUNCHER: " << prefix << std::endl; + + // Log the message to JSON file + logger.log_to_json_file("E_INFO", "LAUNCHER: " + prefix, user, group, command); + //logger.log(E_INFO, "LAUNCHER: " + prefix); return prefix; + } @@ -69,6 +137,7 @@ int lcpex( environment_file_path ); command = prefix; + setup_signal_handlers(); // if we are forcing a pty, then we will use the vpty library if( force_pty ) @@ -91,10 +160,14 @@ int lcpex( */ void set_cloexec_flag(int fd) { + std::string command = "set_cloexec_flag"; if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) { - perror("fcntl"); - exit(1); + std::string error_message = "fcntl(F_SETFD) failed for fd " + std::to_string(fd); + + logger.log_to_json_file("E_FATAL", error_message, user, group, command); + + throw std::runtime_error("fcntl(F_SETFD) failed for fd " + std::to_string(fd)); } } @@ -125,6 +198,13 @@ void set_cloexec_flag(int fd) void run_child_process(bool context_override, const char* context_user, const char* context_group, char* processed_command[], int fd_child_stdout_pipe[], int fd_child_stderr_pipe[]) { while ((dup2(fd_child_stdout_pipe[WRITE_END], STDOUT_FILENO) == -1) && (errno == EINTR)) {} while ((dup2(fd_child_stderr_pipe[WRITE_END], STDERR_FILENO) == -1) && (errno == EINTR)) {} + // Close unused pipe ends to avoid leaks + + close(fd_child_stdout_pipe[READ_END]); + close(fd_child_stdout_pipe[WRITE_END]); + close(fd_child_stderr_pipe[READ_END]); + close(fd_child_stderr_pipe[WRITE_END]); + std::string command = "run_child_process"; // Command context if ( context_override ) { int context_result = set_identity_context(context_user, context_group); @@ -132,34 +212,38 @@ void run_child_process(bool context_override, const char* context_user, const ch case IDENTITY_CONTEXT_ERRORS::ERROR_NONE: break; case IDENTITY_CONTEXT_ERRORS::ERROR_NO_SUCH_USER: - std::cerr << "REX: Aborting: context user not found: " << context_user << std::endl; - exit(1); + logger.log(E_FATAL, "Aborting: context user not found: " + std::string(context_user)); + logger.log_to_json_file("E_FATAL", "Aborting: context user not found: " + std::string(context_user), user, group, command); + _exit(1); break; case IDENTITY_CONTEXT_ERRORS::ERROR_NO_SUCH_GROUP: - std::cerr << "REX: Aborting: context group not found: " << context_group << std::endl; - exit(1); + logger.log(E_FATAL, "Aborting: context group not found: " + std::string(context_group)); + logger.log_to_json_file("E_FATAL", "Aborting: context group not found: " + std::string(context_group), user, group, command); + _exit(1); break; case IDENTITY_CONTEXT_ERRORS::ERROR_SETGID_FAILED: - std::cerr << "REX: Aborting: Setting GID failed: " << context_user << "/" << context_group << std::endl; - exit(1); + logger.log(E_FATAL, "Aborting: Setting GID failed: " + std::string(context_user) + "/" + std::string(context_group)); + logger.log_to_json_file("E_FATAL", "Aborting: Setting GID failed: " + std::string(context_user) + "/" + std::string(context_group), user, group, command); + _exit(1); break; case IDENTITY_CONTEXT_ERRORS::ERROR_SETUID_FAILED: - std::cerr << "REX: Aborting: Setting UID failed: " << context_user << "/" << context_group << std::endl; - exit(1); + logger.log(E_FATAL, "Aborting: Setting UID failed: " + std::string(context_user) + "/" + std::string(context_group)); + logger.log_to_json_file("E_FATAL", "Aborting: Setting UID failed: " + std::string(context_user) + "/" + std::string(context_group), user, group, command); + _exit(1); break; default: - std::cerr << "REX: Aborting: Unknown error while setting identity context." << std::endl; - exit(1); + logger.log(E_FATAL, "Aborting: Unknown error while setting identity context."); + logger.log_to_json_file("E_FATAL", "Aborting: Unknown error while setting identity context.", user, group, command); + _exit(1); break; } } - int exit_code = execvp(processed_command[0], processed_command); - perror("failed on execvp in child"); - exit(exit_code); + logger.log_to_json_file("E_FATAL", "failed on execvp in child", user, group, command); + logger.log(E_FATAL, "failed on execvp in child"); + _Exit(exit_code); } - int execute( std::string command, FILE * stdout_log_fh, @@ -169,6 +253,8 @@ int execute( std::string context_group, bool environment_supplied ){ + + try { // this does three things: // - execute a dang string as a subprocess command // - capture child stdout/stderr to respective log files @@ -190,13 +276,13 @@ int execute( int fd_child_stderr_pipe[2]; if ( pipe(fd_child_stdout_pipe ) == -1 ) { - perror( "child stdout pipe" ); - exit( 1 ); + throw std::runtime_error("Failed to create stdout pipe"); } if ( pipe( fd_child_stderr_pipe ) == -1 ) { - perror( "child stderr pipe" ); - exit( 1 ); + close(fd_child_stdout_pipe[0]); + close(fd_child_stdout_pipe[1]); + throw std::runtime_error("Failed to create stderr pipe"); } // using O_CLOEXEC to ensure that the child process closes the file descriptors @@ -206,15 +292,20 @@ int execute( set_cloexec_flag( fd_child_stderr_pipe[WRITE_END] ); // status result basket for the parent process to capture the child's exit status - int status = 616; + int status; pid_t pid = fork(); switch( pid ) { case -1: { // fork failed - perror("fork failure"); - exit(1); + logger.log(E_FATAL, "fork failure: " + std::string(strerror(errno))); + logger.log_to_json_file("E_FATAL", "fork failure: " + std::string(strerror(errno)), user, group, command); + close(fd_child_stdout_pipe[0]); // Pipe Leaks on Error Paths + close(fd_child_stdout_pipe[1]); // Pipe Leaks on Error Paths + close(fd_child_stderr_pipe[0]); // Pipe Leaks on Error Paths + close(fd_child_stderr_pipe[1]); // Pipe Leaks on Error Paths + throw std::runtime_error("fork() failed: " + std::string(strerror(errno))); } case 0: @@ -234,14 +325,23 @@ int execute( { // parent process + FdGuard stdout_read_guard(fd_child_stdout_pipe[READ_END]); + FdGuard stderr_read_guard(fd_child_stderr_pipe[READ_END]); + FdGuard stdout_write_guard(fd_child_stdout_pipe[WRITE_END]); + FdGuard stderr_write_guard(fd_child_stderr_pipe[WRITE_END]); + + // We don't need WRITE_ENDs in parent, close them now + stdout_write_guard.reset(); // Closes and sets fd to -1 + stderr_write_guard.reset(); + // The parent process has no need to access the entrance to the pipe, so fd_child_*_pipe[1|0] should be closed // within that process too: - close(fd_child_stdout_pipe[WRITE_END]); - close(fd_child_stderr_pipe[WRITE_END]); + // attempt to write to stdout,stderr from child as well as to write each to file - char buf[BUFFER_SIZE]; - + char buf[BUFFER_SIZE] = {0}; + std::strncpy(buf, command.c_str(), BUFFER_SIZE - 1); + buf[BUFFER_SIZE - 1] = '\0'; // contains the byte count of the last read from the pipe ssize_t byte_count; @@ -251,11 +351,11 @@ int execute( // populate the watched_fds array // child STDOUT to parent - watched_fds[CHILD_PIPE_NAMES::STDOUT_READ].fd = fd_child_stdout_pipe[READ_END]; + watched_fds[CHILD_PIPE_NAMES::STDOUT_READ].fd = stdout_read_guard.get(); watched_fds[CHILD_PIPE_NAMES::STDOUT_READ].events = POLLIN; // child STDERR to parent - watched_fds[CHILD_PIPE_NAMES::STDERR_READ].fd = fd_child_stderr_pipe[READ_END]; + watched_fds[CHILD_PIPE_NAMES::STDERR_READ].fd = stderr_read_guard.get(); watched_fds[CHILD_PIPE_NAMES::STDERR_READ].events = POLLIN; // number of files poll() reports as ready @@ -275,8 +375,9 @@ int execute( if (num_files_readable == -1) { // error occurred in poll() - perror("poll"); - exit(1); + logger.log(E_FATAL, "poll() failed: " + std::string(strerror(errno))); + logger.log_to_json_file("E_FATAL", "poll() failed", user, group, command); + throw std::runtime_error("poll() failed"); } if (num_files_readable == 0) { // poll reports no files readable @@ -291,7 +392,10 @@ int execute( if (byte_count == -1) { if (errno == EAGAIN) { continue; } else { // error reading from pipe - perror("read"); + logger.log(E_FATAL, "Error while reading: " + std::string(strerror(errno))); + logger.log_to_json_file("E_FATAL", "Error while reading from pipe", user, group, command); + stdout_read_guard.reset(-1); + stderr_read_guard.reset(-1); exit(EXIT_FAILURE); } @@ -312,13 +416,17 @@ int execute( write_all(STDERR_FILENO, buf, byte_count); } else { // this should never happen - perror("Logic error!"); + logger.log(E_FATAL, "Logic error: unexpected pipe index."); exit(EXIT_FAILURE); } } } if (watched_fds[this_fd].revents & POLLERR) { - close(watched_fds[this_fd].fd); + if (this_fd == CHILD_PIPE_NAMES::STDOUT_READ) { + stdout_read_guard.reset(-1); + } else if (this_fd == CHILD_PIPE_NAMES::STDERR_READ) { + stderr_read_guard.reset(-1); + } break_out = true; } if (watched_fds[this_fd].revents & POLLHUP) { @@ -333,21 +441,46 @@ int execute( waitpid(pid, &status, 0); // Drain the pipes before exiting - while ((byte_count = read(fd_child_stdout_pipe[READ_END], buf, BUFFER_SIZE)) > 0) { + while ((byte_count = read(stdout_read_guard.get(), buf, BUFFER_SIZE)) > 0) { write_all(stdout_log_fh->_fileno, buf, byte_count); write_all(STDOUT_FILENO, buf, byte_count); } - while ((byte_count = read(fd_child_stderr_pipe[READ_END], buf, BUFFER_SIZE)) > 0) { + while ((byte_count = read(stderr_read_guard.get(), buf, BUFFER_SIZE)) > 0) { write_all(stderr_log_fh->_fileno, buf, byte_count); write_all(STDERR_FILENO, buf, byte_count); } - if WIFEXITED(status) { - return WEXITSTATUS(status); - } else { - return -617; + int status; + pid_t pid = waitpid(-1, &status, WNOHANG); // Non-blocking wait for child process + if (pid > 0) { // If a child process has terminated + // Check if the child process exited normally + if (WIFEXITED(status)) { + int exit_code = WEXITSTATUS(status); + logger.log(E_INFO, "Child exited with status " + std::to_string(exit_code)); + logger.log_to_json_file("E_INFO", "Child exited with status " + std::to_string(exit_code), user, group, command); + } + // Check if the child process was terminated by a signal + else if (WIFSIGNALED(status)) { + int signal_number = WTERMSIG(status); + logger.log(E_FATAL, "Process terminated by signal: " + std::to_string(signal_number)); + logger.log_to_json_file("E_FATAL", "Process terminated by signal: " + std::to_string(signal_number), user, group, command); + // Reset signal handler to default before exiting + signal(signal_number, SIG_DFL); + // Return 128 + signal number as the exit code for signal termination + return 128 + signal_number; // POSIX exit code format + } + // Handle unexpected exit conditions + else { + logger.log(E_WARN, "Unknown child exit condition."); + logger.log_to_json_file("E_WARN", "Unknown child exit condition", user, group, command); + } } } } + return 0; + } catch (const std::exception& ex) { + std::cerr << "[LCPEX ERROR] " << ex.what() << std::endl; + return -999; +} } diff --git a/src/lcpex/vpty/libclpex_tty.cpp b/src/lcpex/vpty/libclpex_tty.cpp index a423535..72f47bf 100644 --- a/src/lcpex/vpty/libclpex_tty.cpp +++ b/src/lcpex/vpty/libclpex_tty.cpp @@ -1,4 +1,67 @@ #include "libclpex_tty.h" +#include +#include +#include +#include +#include "../../logger/Logger.h" +#include "../FileHandle.h" +#define BUFFER_SIZE 1024 + +// Initialize logger +static Logger tty_logger(E_INFO, "lcpex_tty"); +std::string tty_user = tty_logger.get_user_name(); +std::string tty_group = tty_logger.get_group_name(); + + +void tty_signal_handler(int sig) { + std::string command = "tty_signal_handler"; // Command context + // Create an instance of Logger (you can change the log level and mask accordingly) + // Log the signal handling + if (sig == SIGCHLD) { + // Log that SIGCHLD was received, but leave the exit status to the parent + tty_logger.log_to_json_file("E_INFO", "SIGCHLD received. A child process ended.", tty_user, tty_group, command); + } else if (sig == SIGINT) { + tty_logger.log_to_json_file("E_FATAL", "SIGINT received. Gracefully terminating...", tty_user, tty_group, command); + } else if (sig == SIGTERM) { + tty_logger.log_to_json_file("E_FATAL", "SIGTERM received. Terminating...", tty_user, tty_group, command); + } else if (sig == SIGSEGV) { + tty_logger.log_to_json_file("E_FATAL", "SIGSEGV received. Possible segmentation fault.", tty_user, tty_group, command); + } else { + tty_logger.log_to_json_file("E_FATAL", "Unhandled signal received", tty_user, tty_group, command); + } +} +// Setup signal registrations +void setup_tty_signal_handlers() { + struct sigaction sa; + sa.sa_handler = tty_signal_handler; // <-- handler function + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_RESTART | SA_NOCLDSTOP; + std::string command = "setup_tty_signal_handlers"; // Command context + + // SIGCHLD + if (sigaction(SIGCHLD, &sa, nullptr) == -1) { + std::string error_message = "Failed to set SIGCHLD handler: " + std::string(strerror(errno)); + tty_logger.log_to_json_file("E_FATAL", error_message, tty_user, tty_group, command); // Log to JSON file + } + + // SIGINT + if (sigaction(SIGINT, &sa, nullptr) == -1) { + std::string error_message = "Failed to set SIGINT handler: " + std::string(strerror(errno)); + tty_logger.log_to_json_file("E_FATAL", error_message, tty_user, tty_group, command); // Log to JSON file + } + + // SIGTERM + if (sigaction(SIGTERM, &sa, nullptr) == -1) { + std::string error_message = "Failed to set SIGTERM handler: " + std::string(strerror(errno)); + tty_logger.log_to_json_file("E_FATAL", error_message, tty_user, tty_group, command); // Log to JSON file + } + + // SIGSEGV + if (sigaction(SIGSEGV, &sa, nullptr) == -1) { + std::string error_message = "Failed to set SIGSEGV handler: " + std::string(strerror(errno)); + tty_logger.log_to_json_file("E_FATAL", error_message, tty_user, tty_group, command); // Log to JSON file + } +} /** * @brief Safely prints a message and exits the program @@ -11,13 +74,26 @@ * Then, it calls `ttyResetExit()` with `ttyOrig` as its argument to reset the terminal settings. * Finally, the function exits the program with exit code 1. */ -void safe_perror( const char * msg, struct termios * ttyOrig ) + +void set_tty_cloexec_flag(int fd) { - std::cerr << msg << std::endl; - ttyResetExit( ttyOrig ); - exit(1); + std::string command = "set_tty_cloexec_flag"; + if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) + { + std::string error_message = "fcntl(F_SETFD) failed for fd " + std::to_string(fd); + tty_logger.log_to_json_file("E_FATAL", error_message, tty_user, tty_group, command); + throw std::runtime_error("fcntl(F_SETFD) failed for fd " + std::to_string(fd)); + } } +void safe_perror( const char * msg, struct termios * ttyOrig ) +{ + std::string command = "safe_perror"; // Command context + tty_logger.log_to_json_file("E_FATAL", msg, tty_user, tty_group, command); // Log the message before exiting + std::cerr << msg << std::endl; + ttyResetExit( ttyOrig ); + throw std::runtime_error(std::string("TTY Error: ") + msg); +} /** * @brief Executes the child process @@ -44,12 +120,14 @@ void safe_perror( const char * msg, struct termios * ttyOrig ) */ void run_child_process( int fd_child_stderr_pipe[2], char * processed_command[], struct termios * ttyOrig, bool context_override, std::string context_user, std::string context_group ) { + std::string command = "run_child_process_tty"; // Command context // redirect stderr to the write end of the stderr pipe // close the file descriptor STDERR_FILENO if it was previously open, then (re)open it as a copy of while ((dup2(fd_child_stderr_pipe[WRITE_END], STDERR_FILENO) == -1) && (errno == EINTR)) {} - // close the write end of the stderr pipe + // close the write end of the stderr pipe and read end to avoid leaks close( fd_child_stderr_pipe[WRITE_END] ); + close( fd_child_stderr_pipe[READ_END] ); // if the user has specified a context override, set the context if ( context_override ) @@ -59,24 +137,29 @@ void run_child_process( int fd_child_stderr_pipe[2], char * processed_command[], case IDENTITY_CONTEXT_ERRORS::ERROR_NONE: break; case IDENTITY_CONTEXT_ERRORS::ERROR_NO_SUCH_USER: - std::cerr << "lcpex: Aborting: context user not found: " << context_user << std::endl; - exit(1); + tty_logger.log(E_FATAL, "Aborting: context user not found: " + context_user); + tty_logger.log_to_json_file("E_FATAL", "Context user not found: " + context_user, tty_user, tty_group, command); + _exit(1); break; case IDENTITY_CONTEXT_ERRORS::ERROR_NO_SUCH_GROUP: - std::cerr << "lcpex: Aborting: context group not found: " << context_group << std::endl; - exit(1); + tty_logger.log(E_FATAL, "Aborting: context group not found: " + context_group); + tty_logger.log_to_json_file("E_FATAL", "Context group not found: " + context_group, tty_user, tty_group, command); + _exit(1); break; case IDENTITY_CONTEXT_ERRORS::ERROR_SETGID_FAILED: - std::cerr << "lcpex: Aborting: Setting GID failed: " << context_user << "/" << context_group << std::endl; - exit(1); + tty_logger.log(E_FATAL, "Aborting: Setting GID failed: " + context_user + "/" + context_group); + tty_logger.log_to_json_file("E_FATAL", "Setting GID failed for: " + context_user + "/" + context_group, tty_user, tty_group, command); + _exit(1); break; case IDENTITY_CONTEXT_ERRORS::ERROR_SETUID_FAILED: - std::cerr << "lcpex: Aborting: Setting UID failed: " << context_user << "/" << context_group << std::endl; - exit(1); + tty_logger.log(E_FATAL, "Aborting: Setting UID failed: " + context_user + "/" + context_group); + tty_logger.log_to_json_file("E_FATAL", "Setting UID failed for: " + context_user + "/" + context_group, tty_user, tty_group, command); + _exit(1); break; default: - std::cerr << "lcpex: Aborting: Unknown error while setting identity context." << std::endl; - exit(1); + tty_logger.log(E_FATAL, "Aborting: Unknown error while setting identity context."); + tty_logger.log_to_json_file("E_FATAL", "Unknown error while setting identity context.", tty_user, tty_group, command); + _exit(1); break; } } @@ -84,8 +167,10 @@ void run_child_process( int fd_child_stderr_pipe[2], char * processed_command[], // execute the dang command, print to stdout, stderr (of parent), and dump to file for each!!!! // (and capture exit code in parent) int exit_code = execvp( processed_command[0], processed_command ); - safe_perror("failed on execvp in child", ttyOrig ); - exit(exit_code); + tty_logger.log_to_json_file("E_FATAL", "failed on execvp in child", tty_user, tty_group, command); + tty_logger.log(E_FATAL, "failed on execvp in child"); + _Exit(exit_code); + } // this does three things: @@ -101,6 +186,11 @@ int exec_pty( std::string context_group, bool environment_supplied ) { + try { + std::string exec_command = "exec_pty"; // Command context + tty_logger.log_to_json_file("E_INFO", "TTY LAUNCHER: " + command, tty_user, tty_group, exec_command); + // Setup signal handlers + setup_tty_signal_handlers(); // initialize the terminal settings obj struct termios ttyOrig; @@ -115,13 +205,13 @@ int exec_pty( char ** processed_command = expand_env( command ); if ( stdout_log_fh == NULL ) { - safe_perror( "Error opening STDOUT log file. Aborting.", &ttyOrig ); - exit( 1 ); + tty_logger.log_to_json_file("E_FATAL", "Error opening STDOUT log file. Aborting.", tty_user, tty_group, exec_command); + throw std::runtime_error("Error opening STDOUT log file"); } if ( stderr_log_fh == NULL ) { - safe_perror( "Error opening STDERR log file. Aborting.", &ttyOrig ); - exit( 1 ); + tty_logger.log_to_json_file("E_FATAL", "Error opening STDERR log file. Aborting.", tty_user, tty_group, exec_command); + throw std::runtime_error("Error opening STDERR log file"); } @@ -129,16 +219,13 @@ int exec_pty( int fd_child_stderr_pipe[2]; if ( pipe( fd_child_stderr_pipe ) == -1 ) { - safe_perror( "child stderr pipe", &ttyOrig ); - exit( 1 ); + std::string error_message = "Failed to create stderr pipe: " + std::string(strerror(errno)); + tty_logger.log_to_json_file("E_FATAL", error_message, tty_user, tty_group, exec_command); + throw std::runtime_error("Failed to create stderr pipe"); } - - // using O_CLOEXEC to ensure that the child process closes the file descriptors - if ( fcntl( fd_child_stderr_pipe[READ_END], F_SETFD, FD_CLOEXEC ) == -1 ) { perror("fcntl"); exit(1); } - - // // same for stderr write - if ( fcntl( fd_child_stderr_pipe[WRITE_END], F_SETFD, FD_CLOEXEC ) == -1 ) { perror("fcntl"); exit(1); } - + // using O_CLOEXEC to ensure that the child process closes the file descriptors + set_tty_cloexec_flag( fd_child_stderr_pipe[READ_END] ); + set_tty_cloexec_flag( fd_child_stderr_pipe[WRITE_END] ); // status result basket for the parent process to capture the child's exit status int status = 616; @@ -148,39 +235,52 @@ int exec_pty( struct winsize ws; /* Retrieve the attributes of terminal on which we are started */ - if (tcgetattr(STDIN_FILENO, &ttyOrig) == -1) - safe_perror("tcgetattr", &ttyOrig); + if (tcgetattr(STDIN_FILENO, &ttyOrig) == -1){ + tty_logger.log_to_json_file("E_FATAL", "tcgetattr failed", tty_user, tty_group, exec_command); + close(fd_child_stderr_pipe[READ_END]); // Pipe Leaks on Error Paths + close(fd_child_stderr_pipe[WRITE_END]); // Pipe Leaks on Error Paths + throw std::runtime_error("tcgetattr failed"); +} if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) < 0) - safe_perror("ioctl-TIOCGWINSZ", &ttyOrig ); - +{ + tty_logger.log_to_json_file("E_FATAL", "ioctl-TIOCGWINSZ failed", tty_user, tty_group, exec_command); + close(fd_child_stderr_pipe[READ_END]); // Pipe Leaks on Error Paths + close(fd_child_stderr_pipe[WRITE_END]); // Pipe Leaks on Error Paths + throw std::runtime_error("ioctl-TIOCGWINSZ failed"); +} pid_t pid = ptyFork( &masterFd, slaveName, MAX_SNAME, &ttyOrig, &ws ); - switch( pid ) { case -1: { // fork failed - safe_perror("ptyfork failure", &ttyOrig ); - exit(1); + tty_logger.log(E_FATAL, "ptyfork failure: " + std::string(strerror(errno))); + tty_logger.log_to_json_file("E_FATAL", "ptyfork failure: " + std::string(strerror(errno)), tty_user, tty_group, exec_command); + close(fd_child_stderr_pipe[READ_END]); // Pipe Leaks on Error Paths + close(fd_child_stderr_pipe[WRITE_END]); // Pipe Leaks on Error Paths + throw std::runtime_error("ptyfork() failed: " + std::string(strerror(errno))); } - case 0: { // child process run_child_process( fd_child_stderr_pipe, processed_command, &ttyOrig, context_override, context_user, context_group ); } - default: { // parent process // start ptyfork integration ttySetRaw(STDIN_FILENO, &ttyOrig); + // RAII guards for file descriptors + FdGuard stderr_read_guard(fd_child_stderr_pipe[READ_END]); + FdGuard stderr_write_guard(fd_child_stderr_pipe[WRITE_END]); + FdGuard master_fd_guard(masterFd); - // The parent process has no need to access the entrance to the pipe - close(fd_child_stderr_pipe[WRITE_END]); - + // We don't need WRITE_END in parent, close it now + stderr_write_guard.reset(); // Closes and sets fd to -1 // attempt to write to stdout,stderr from child as well as to write each to file - char buf[BUFFER_SIZE]; + char buf[BUFFER_SIZE] = {0}; + std::strncpy(buf, command.c_str(), BUFFER_SIZE - 1); + buf[BUFFER_SIZE - 1] = '\0'; // contains the byte count of the last read from the pipe ssize_t byte_count; @@ -195,11 +295,11 @@ int exec_pty( watched_fds[0].events = POLLIN; // child pty to parent (stdout) - watched_fds[1].fd = masterFd; + watched_fds[1].fd = master_fd_guard.get(); watched_fds[1].events = POLLIN; // child stderr to parent - watched_fds[2].fd = fd_child_stderr_pipe[READ_END]; + watched_fds[2].fd = stderr_read_guard.get(); watched_fds[2].events = POLLIN; // number of files poll() reports as ready @@ -219,8 +319,10 @@ int exec_pty( if (num_files_readable == -1) { // error occurred in poll() - safe_perror("poll", &ttyOrig ); - exit(1); + tty_logger.log(E_FATAL, "poll() failed: " + std::string(strerror(errno))); + tty_logger.log_to_json_file("E_FATAL", "poll() failed", tty_user, tty_group, exec_command); + ttyResetExit( &ttyOrig); + throw std::runtime_error("poll() failed"); } if (num_files_readable == 0) { @@ -237,7 +339,11 @@ int exec_pty( if (byte_count == -1) { if (errno == EAGAIN) { continue; } else { // error reading from pipe - safe_perror("read", &ttyOrig ); + tty_logger.log(E_FATAL, "Error while reading: " + std::string(strerror(errno))); + tty_logger.log_to_json_file("E_FATAL", "Error while reading from pipe", tty_user, tty_group, exec_command); + ttyResetExit( &ttyOrig); + stderr_read_guard.reset(-1); + master_fd_guard.reset(-1); exit(EXIT_FAILURE); } } else if (byte_count == 0) { @@ -261,12 +367,17 @@ int exec_pty( write_all(STDERR_FILENO, buf, byte_count); } else { // this should never happen - perror("Logic error!"); + tty_logger.log(E_FATAL, "Logic error: unexpected pipe index."); exit(EXIT_FAILURE); } } } if (watched_fds[this_fd].revents & POLLERR) { + if (this_fd == 1) { + master_fd_guard.reset(-1); + } else if (this_fd == 2) { + stderr_read_guard.reset(-1); + } //close(watched_fds[this_fd].fd); break_out = true; //continue; @@ -288,15 +399,42 @@ int exec_pty( // wait for child to exit, capture status waitpid(pid, &status, 0); - while ((byte_count = read(watched_fds[1].fd, buf, BUFFER_SIZE)) > 0) { + while ((byte_count = read(master_fd_guard.get(), buf, BUFFER_SIZE)) > 0) { write_all(stdout_log_fh->_fileno, buf, byte_count); write_all(STDOUT_FILENO, buf, byte_count); } - while ((byte_count = read(fd_child_stderr_pipe[READ_END], buf, BUFFER_SIZE)) > 0) { + while ((byte_count = read(stderr_read_guard.get(), buf, BUFFER_SIZE)) > 0) { write_all(stderr_log_fh->_fileno, buf, byte_count); write_all(STDERR_FILENO, buf, byte_count); } + // Handle child process status with proper signal handling + pid_t child_pid = waitpid(-1, &status, WNOHANG); // Non-blocking wait for child process + if (child_pid > 0) { // If a child process has terminated + // Check if the child process exited normally + if (WIFEXITED(status)) { + int exit_code = WEXITSTATUS(status); + tty_logger.log(E_INFO, "Child exited with status " + std::to_string(exit_code)); + tty_logger.log_to_json_file("E_INFO", "Child exited with status " + std::to_string(exit_code), tty_user, tty_group, exec_command); + ttyResetExit( &ttyOrig); + return exit_code; + } + // Check if the child process was terminated by a signal + else if (WIFSIGNALED(status)) { + int signal_number = WTERMSIG(status); + tty_logger.log(E_FATAL, "Process terminated by signal: " + std::to_string(signal_number)); + tty_logger.log_to_json_file("E_FATAL", "Process terminated by signal: " + std::to_string(signal_number), tty_user, tty_group, exec_command); + // Reset signal handler to default before exiting + signal(signal_number, SIG_DFL); + ttyResetExit( &ttyOrig); + return 128 + signal_number; // POSIX exit code format + } + // Handle unexpected exit conditions + else { + tty_logger.log(E_WARN, "Unknown child exit condition."); + tty_logger.log_to_json_file("E_WARN", "Unknown child exit condition", tty_user, tty_group, exec_command); + } + } ttyResetExit( &ttyOrig); if WIFEXITED(status) { @@ -306,4 +444,9 @@ int exec_pty( } } } + return 0; + } catch (const std::exception& ex) { + std::cerr << "[LCPEX TTY ERROR] " << ex.what() << std::endl; + return -999; + } } \ No newline at end of file diff --git a/src/logger/Logger.cpp b/src/logger/Logger.cpp index 8b022d4..6f4cc34 100644 --- a/src/logger/Logger.cpp +++ b/src/logger/Logger.cpp @@ -19,6 +19,13 @@ */ #include "Logger.h" +#include +#include +#include +#include +#include // For FdGuard +#include // For O_RDONLY, O_WRONLY, open() +#include "../lcpex/FileHandle.h" // Include the FdGuard header Logger::Logger( int LOG_LEVEL, std::string mask ) { @@ -26,6 +33,11 @@ Logger::Logger( int LOG_LEVEL, std::string mask ) this->mask = mask; } +// Destructor added in the update +Logger::~Logger() { + +} + void Logger::log( int LOG_LEVEL, std::string msg ) { std::string ERR = "XXXX"; @@ -42,7 +54,7 @@ void Logger::log( int LOG_LEVEL, std::string msg ) std::string s_msg = "[" + ERR + "] " + msg; - if ( LOG_LEVEL == E_FATAL | LOG_LEVEL == E_WARN ) + if ( LOG_LEVEL == E_FATAL || LOG_LEVEL == E_WARN ) { std::cerr << "[" << get_8601() << "] [" << ERR << "] " << "[" << this->mask << "] " << msg.c_str() << std::endl; } else { @@ -53,6 +65,140 @@ void Logger::log( int LOG_LEVEL, std::string msg ) void Logger::log_task( int LOG_LEVEL, std::string task_name, std::string msg ) { - std::string final_msg = "[" + task_name + "] " + msg; - this->log( LOG_LEVEL, final_msg ); + // Added memory management from the updated file + + size_t task_msg_len = task_name.length() + msg.length() + 4; + char* task_msg = (char*)malloc(task_msg_len); + + if (task_msg) { + snprintf(task_msg, task_msg_len, "[%s] %s", task_name.c_str(), msg.c_str()); + log(LOG_LEVEL, task_msg); + free(task_msg); + } } + +// JSON logging method +void Logger::create_json_log_entry(char* buffer, size_t buffer_size, + const char* log_level, const char* message, + const char* user, const char* group, + const char* command) +{ + std::string timestamp = get_current_timestamp(); + snprintf(buffer, buffer_size, + "{\n" + " \"timestamp\": \"%s\",\n" + " \"log_level\": \"%s\",\n" + " \"message\": \"%s\",\n" + " \"context\": {\n" + " \"user\": \"%s\",\n" + " \"group\": \"%s\",\n" + " \"command\": \"%s\"\n" + " }\n" + "}", + timestamp.c_str(), log_level, message, user, group, command); +} + +// Add methods for user/group name and timestamp +std::string Logger::get_user_name() +{ + struct passwd* pw = getpwuid(getuid()); + if (pw) { + return std::string(pw->pw_name); + } + return "unknown"; +} + +std::string Logger::get_group_name() +{ + struct group* grp = getgrgid(getgid()); + if (grp) { + return std::string(grp->gr_name); + } + return "unknown"; +} + +std::string Logger::get_current_timestamp() +{ + time_t now = time(NULL); + struct tm* timeinfo = localtime(&now); + char buffer[64]; + strftime(buffer, sizeof(buffer), "%Y-%m-%d_%H:%M:%S", timeinfo); + return std::string(buffer); +} + +//Log to JSON file +void Logger::log_to_json_file(const std::string& log_level, const std::string& message, + const std::string& user, const std::string& group, + const std::string& command, bool log_to_console) +{ + // Log to console if requested + const char* log_level_str = "UNKNOWN"; + if (log_level == "E_INFO") log_level_str = "INFO"; + else if (log_level == "E_FATAL") log_level_str = "FATAL"; + else if (log_level == "E_WARN") log_level_str = "WARN"; + else if (log_level == "E_DEBUG") log_level_str = "DEBUG"; + + if (log_to_console && (log_level_str == "INFO" || log_level_str == "FATAL")) { + std::string timestamp = get_current_timestamp(); + printf("[%s] [%s] [%s] %s\n", timestamp.c_str(), log_level_str, this->mask.c_str(), message.c_str()); + fflush(stdout); + } + + // 2. Create JSON log entry + char json_log[2048]; + create_json_log_entry(json_log, sizeof(json_log), log_level_str, message.c_str(), user.c_str(), group.c_str(), command.c_str()); + + // 3. Generate filename + std::string timestamp = get_current_timestamp(); + std::string filename = "log_" + timestamp + ".json"; + + // 4. Use FdGuard for file handling (to manage file opening and closing) + FdGuard check_file(open(filename.c_str(), O_RDONLY)); + bool file_empty = true; + + if (check_file.get() != -1) { + off_t file_size = lseek(check_file.get(), 0, SEEK_END); + file_empty = (file_size == 0); + } + + if (file_empty) { + // First entry - create new JSON array + FdGuard json_file(open(filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0644)); + if (json_file.get() != -1) { + dprintf(json_file.get(), "[\n %s\n]\n", json_log); + } else { + fprintf(stderr, "Failed to open log file for writing: %s\n", filename.c_str()); + } + } else { + // Subsequent entries - handle JSON array properly + FdGuard read_file(open(filename.c_str(), O_RDONLY)); + if (read_file.get() == -1) { + fprintf(stderr, "Failed to open log file for reading: %s\n", filename.c_str()); + return; + } + + off_t file_size = lseek(read_file.get(), 0, SEEK_END); + lseek(read_file.get(), 0, SEEK_SET); + char* content = (char*)malloc(file_size + 1); + if (!content) { + fprintf(stderr, "Failed to allocate memory for file content\n"); + return; + } + read(read_file.get(), content, file_size); + content[file_size] = '\0'; + // Find and remove the last ']' + char* last_bracket = strrchr(content, ']'); + if (last_bracket) { + *last_bracket = '\0'; + } + FdGuard write_file(open(filename.c_str(), O_WRONLY | O_TRUNC, 0644)); + if (write_file.get() != -1) { + dprintf(write_file.get(), "%s,\n %s\n]\n", content, json_log); + } else { + fprintf(stderr, "Failed to open log file for writing: %s\n", filename.c_str()); + } + free(content); + } +} + +} // namespace lcpex \ No newline at end of file diff --git a/src/logger/Logger.h b/src/logger/Logger.h index f4c5330..ea79b5f 100644 --- a/src/logger/Logger.h +++ b/src/logger/Logger.h @@ -24,8 +24,14 @@ #include #include -#include -#include +#include +#include // For O_RDONLY, O_WRONLY, open() +#include +#include +#include +#include +#include +#include // for va_list in FileGuard::printf #include "../misc/helpers.h" enum L_LVL { @@ -37,15 +43,28 @@ enum L_LVL { class Logger { public: - Logger( int LOG_LEVEL, std::string mask ); - void log( int LOG_LEVEL, std::string msg ); - void log_task( int LOG_LEVEL, std::string task_name, std::string msg ); + // Constructor and Destructor + Logger(int LOG_LEVEL, std::string mask); + ~Logger(); // Added Destructor + // Logging methods + void log(int LOG_LEVEL, std::string msg); + void log_task(int LOG_LEVEL, std::string task_name, std::string msg); + void log_to_json_file(const std::string& log_level, const std::string& message, + const std::string& user, const std::string& group, + const std::string& command, bool log_to_console = true); + // Helper methods + std::string get_current_timestamp(); + std::string get_user_name(); + std::string get_group_name(); private: int LOG_LEVEL; std::string mask; + // Internal helper methods + void create_json_log_entry(char* buffer, size_t buffer_size, + const char* log_level, const char* message, + const char* user, const char* group, + const char* command); }; - - #endif //REX_LOGGER_H diff --git a/tatus b/tatus new file mode 100644 index 0000000..0f3168d --- /dev/null +++ b/tatus @@ -0,0 +1,370 @@ +diff --git a/src/lcpex/liblcpex.cpp b/src/lcpex/liblcpex.cpp +index b0e033e..db79c19 100644 +--- a/src/lcpex/liblcpex.cpp ++++ b/src/lcpex/liblcpex.cpp +@@ -1,5 +1,69 @@ + #include "liblcpex.h" ++#include  ++#include  ++#include  ++#include  ++#include "../logger/Logger.h" ++#include "FileHandle.h" ++#define BUFFER_SIZE 1024 ++ ++ ++// Initialize logger ++static Logger logger(E_INFO, "lcpex"); ++std::string user = logger.get_user_name(); ++std::string group = logger.get_group_name(); ++ ++void signal_handler(int sig) { ++ std::string command = "signal_handler"; // Command context ++ ++ // Create an instance of Logger (you can change the log level and mask accordingly) ++ // Log the signal handling ++ if (sig == SIGCHLD) { ++ // Log that SIGCHLD was received, but leave the exit status to the parent ++ logger.log_to_json_file("E_INFO", "SIGCHLD received. A child process ended.", user, group, command); ++ } else if (sig == SIGINT) { ++ logger.log_to_json_file("E_FATAL", "SIGINT received. Gracefully terminating...", user, group, command); ++ } else if (sig == SIGTERM) { ++ logger.log_to_json_file("E_FATAL", "SIGTERM received. Terminating...", user, group, command); ++ } else if (sig == SIGSEGV) { ++ logger.log_to_json_file("E_FATAL", "SIGSEGV received. Possible segmentation fault.", user, group, command); ++ } else { ++ logger.log_to_json_file("E_FATAL", "Unhandled signal received", user, group, command); ++ } ++} ++ ++// Setup signal registrations ++void setup_signal_handlers() { ++ struct sigaction sa; ++ sa.sa_handler = signal_handler; // <-- handler function ++ sigemptyset(&sa.sa_mask); ++ sa.sa_flags = SA_RESTART | SA_NOCLDSTOP; ++ std::string command = "setup_signal_handlers"; // Command context ++ ++ // SIGCHLD ++ if (sigaction(SIGCHLD, &sa, nullptr) == -1) { ++ std::string error_message = "Failed to set SIGCHLD handler: " + std::string(strerror(errno)); ++ logger.log_to_json_file("E_FATAL", error_message, user, group, command); // Log to JSON file ++ } ++ ++ // SIGINT ++ if (sigaction(SIGINT, &sa, nullptr) == -1) { ++ std::string error_message = "Failed to set SIGINT handler: " + std::string(strerror(errno)); ++ logger.log_to_json_file("E_FATAL", error_message, user, group, command); // Log to JSON file ++ } +  ++ // SIGTERM ++ if (sigaction(SIGTERM, &sa, nullptr) == -1) { ++ std::string error_message = "Failed to set SIGTERM handler: " + std::string(strerror(errno)); ++ logger.log_to_json_file("E_FATAL", error_message, user, group, command); // Log to JSON file ++ } ++ ++ // SIGSEGV ++ if (sigaction(SIGSEGV, &sa, nullptr) == -1) { ++ std::string error_message = "Failed to set SIGSEGV handler: " + std::string(strerror(errno)); ++ logger.log_to_json_file("E_FATAL", error_message, user, group, command); // Log to JSON file ++ } ++} +  + std::string prefix_generator( + std::string command, +@@ -37,8 +101,12 @@ std::string prefix_generator( + // it's not a shell command, so we can just execute it directly + prefix = command; + } +- std::cout << "LAUNCHER: " << prefix << std::endl; ++ ++ // Log the message to JSON file ++ logger.log_to_json_file("E_INFO", "LAUNCHER: " + prefix, user, group, command); ++ //logger.log(E_INFO, "LAUNCHER: " + prefix); + return prefix; ++ + } +  +  +@@ -69,6 +137,7 @@ int lcpex( + environment_file_path + ); + command = prefix; ++ setup_signal_handlers(); +  + // if we are forcing a pty, then we will use the vpty library + if( force_pty ) +@@ -91,10 +160,14 @@ int lcpex( + */ + void set_cloexec_flag(int fd) + { ++ std::string command = "set_cloexec_flag"; + if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) + { +- perror("fcntl"); +- exit(1); ++ std::string error_message = "fcntl(F_SETFD) failed for fd " + std::to_string(fd); ++ ++ logger.log_to_json_file("E_FATAL", error_message, user, group, command); ++ ++ throw std::runtime_error("fcntl(F_SETFD) failed for fd " + std::to_string(fd)); + } + } +  +@@ -125,6 +198,13 @@ void set_cloexec_flag(int fd) + void run_child_process(bool context_override, const char* context_user, const char* context_group, char* processed_command[], int fd_child_stdout_pipe[], int fd_child_stderr_pipe[]) { + while ((dup2(fd_child_stdout_pipe[WRITE_END], STDOUT_FILENO) == -1) && (errno == EINTR)) {} + while ((dup2(fd_child_stderr_pipe[WRITE_END], STDERR_FILENO) == -1) && (errno == EINTR)) {} ++ // Close unused pipe ends to avoid leaks ++ ++ close(fd_child_stdout_pipe[READ_END]); ++ close(fd_child_stdout_pipe[WRITE_END]); ++ close(fd_child_stderr_pipe[READ_END]); ++ close(fd_child_stderr_pipe[WRITE_END]); ++ std::string command = "run_child_process"; // Command context +  + if ( context_override ) { + int context_result = set_identity_context(context_user, context_group); +@@ -132,34 +212,38 @@ void run_child_process(bool context_override, const char* context_user, const ch + case IDENTITY_CONTEXT_ERRORS::ERROR_NONE: + break; + case IDENTITY_CONTEXT_ERRORS::ERROR_NO_SUCH_USER: +- std::cerr << "REX: Aborting: context user not found: " << context_user << std::endl; +- exit(1); ++ logger.log(E_FATAL, "Aborting: context user not found: " + std::string(context_user)); ++ logger.log_to_json_file("E_FATAL", "Aborting: context user not found: " + std::string(context_user), user, group, command); ++ _exit(1); + break; + case IDENTITY_CONTEXT_ERRORS::ERROR_NO_SUCH_GROUP: +- std::cerr << "REX: Aborting: context group not found: " << context_group << std::endl; +- exit(1); ++ logger.log(E_FATAL, "Aborting: context group not found: " + std::string(context_group)); ++ logger.log_to_json_file("E_FATAL", "Aborting: context group not found: " + std::string(context_group), user, group, command); ++ _exit(1); + break; + case IDENTITY_CONTEXT_ERRORS::ERROR_SETGID_FAILED: +- std::cerr << "REX: Aborting: Setting GID failed: " << context_user << "/" << context_group << std::endl; +- exit(1); ++ logger.log(E_FATAL, "Aborting: Setting GID failed: " + std::string(context_user) + "/" + std::string(context_group)); ++ logger.log_to_json_file("E_FATAL", "Aborting: Setting GID failed: " + std::string(context_user) + "/" + std::string(context_group), user, group, command); ++ _exit(1); + break; + case IDENTITY_CONTEXT_ERRORS::ERROR_SETUID_FAILED: +- std::cerr << "REX: Aborting: Setting UID failed: " << context_user << "/" << context_group << std::endl; +- exit(1); ++ logger.log(E_FATAL, "Aborting: Setting UID failed: " + std::string(context_user) + "/" + std::string(context_group)); ++ logger.log_to_json_file("E_FATAL", "Aborting: Setting UID failed: " + std::string(context_user) + "/" + std::string(context_group), user, group, command); ++ _exit(1); + break; + default: +- std::cerr << "REX: Aborting: Unknown error while setting identity context." << std::endl; +- exit(1); ++ logger.log(E_FATAL, "Aborting: Unknown error while setting identity context."); ++ logger.log_to_json_file("E_FATAL", "Aborting: Unknown error while setting identity context.", user, group, command); ++ _exit(1); + break; + } + } +- + int exit_code = execvp(processed_command[0], processed_command); +- perror("failed on execvp in child"); +- exit(exit_code); ++ logger.log_to_json_file("E_FATAL", "failed on execvp in child", user, group, command); ++ logger.log(E_FATAL, "failed on execvp in child"); ++ _Exit(exit_code); + } +  +- + int execute( + std::string command, + FILE * stdout_log_fh, +@@ -169,6 +253,8 @@ int execute( + std::string context_group, + bool environment_supplied + ){ ++ ++ try { + // this does three things: + // - execute a dang string as a subprocess command + // - capture child stdout/stderr to respective log files +@@ -190,13 +276,13 @@ int execute( + int fd_child_stderr_pipe[2]; +  + if ( pipe(fd_child_stdout_pipe ) == -1 ) { +- perror( "child stdout pipe" ); +- exit( 1 ); ++ throw std::runtime_error("Failed to create stdout pipe"); + } +  + if ( pipe( fd_child_stderr_pipe ) == -1 ) { +- perror( "child stderr pipe" ); +- exit( 1 ); ++ close(fd_child_stdout_pipe[0]); ++ close(fd_child_stdout_pipe[1]); ++ throw std::runtime_error("Failed to create stderr pipe"); + } +  + // using O_CLOEXEC to ensure that the child process closes the file descriptors +@@ -206,15 +292,20 @@ int execute( + set_cloexec_flag( fd_child_stderr_pipe[WRITE_END] ); +  + // status result basket for the parent process to capture the child's exit status +- int status = 616; ++ int status; + pid_t pid = fork(); +  + switch( pid ) { + case -1: + { + // fork failed +- perror("fork failure"); +- exit(1); ++ logger.log(E_FATAL, "fork failure: " + std::string(strerror(errno))); ++ logger.log_to_json_file("E_FATAL", "fork failure: " + std::string(strerror(errno)), user, group, command); ++ close(fd_child_stdout_pipe[0]); // Pipe Leaks on Error Paths ++ close(fd_child_stdout_pipe[1]); // Pipe Leaks on Error Paths ++ close(fd_child_stderr_pipe[0]); // Pipe Leaks on Error Paths ++ close(fd_child_stderr_pipe[1]); // Pipe Leaks on Error Paths ++ throw std::runtime_error("fork() failed: " + std::string(strerror(errno))); + } +  + case 0: +@@ -234,14 +325,23 @@ int execute( + { + // parent process +  ++ FdGuard stdout_read_guard(fd_child_stdout_pipe[READ_END]); ++ FdGuard stderr_read_guard(fd_child_stderr_pipe[READ_END]); ++ FdGuard stdout_write_guard(fd_child_stdout_pipe[WRITE_END]); ++ FdGuard stderr_write_guard(fd_child_stderr_pipe[WRITE_END]); ++ ++ // We don't need WRITE_ENDs in parent, close them now ++ stdout_write_guard.reset(); // Closes and sets fd to -1 ++ stderr_write_guard.reset(); ++ + // The parent process has no need to access the entrance to the pipe, so fd_child_*_pipe[1|0] should be closed + // within that process too: +- close(fd_child_stdout_pipe[WRITE_END]); +- close(fd_child_stderr_pipe[WRITE_END]); +  +- // attempt to write to stdout,stderr from child as well as to write each to file +- char buf[BUFFER_SIZE]; +  ++ // attempt to write to stdout,stderr from child as well as to write each to file ++ char buf[BUFFER_SIZE] = {0}; ++ std::strncpy(buf, command.c_str(), BUFFER_SIZE - 1); ++ buf[BUFFER_SIZE - 1] = '\0'; + // contains the byte count of the last read from the pipe + ssize_t byte_count; +  +@@ -251,11 +351,11 @@ int execute( + // populate the watched_fds array +  + // child STDOUT to parent +- watched_fds[CHILD_PIPE_NAMES::STDOUT_READ].fd = fd_child_stdout_pipe[READ_END]; ++ watched_fds[CHILD_PIPE_NAMES::STDOUT_READ].fd = stdout_read_guard.get(); + watched_fds[CHILD_PIPE_NAMES::STDOUT_READ].events = POLLIN; +  + // child STDERR to parent +- watched_fds[CHILD_PIPE_NAMES::STDERR_READ].fd = fd_child_stderr_pipe[READ_END]; ++ watched_fds[CHILD_PIPE_NAMES::STDERR_READ].fd = stderr_read_guard.get(); + watched_fds[CHILD_PIPE_NAMES::STDERR_READ].events = POLLIN; +  + // number of files poll() reports as ready +@@ -275,8 +375,9 @@ int execute( +  + if (num_files_readable == -1) { + // error occurred in poll() +- perror("poll"); +- exit(1); ++ logger.log(E_FATAL, "poll() failed: " + std::string(strerror(errno))); ++ logger.log_to_json_file("E_FATAL", "poll() failed", user, group, command); ++ throw std::runtime_error("poll() failed"); + } + if (num_files_readable == 0) { + // poll reports no files readable +@@ -291,7 +392,10 @@ int execute( + if (byte_count == -1) { + if (errno == EAGAIN) { continue; } else { + // error reading from pipe +- perror("read"); ++ logger.log(E_FATAL, "Error while reading: " + std::string(strerror(errno))); ++ logger.log_to_json_file("E_FATAL", "Error while reading from pipe", user, group, command); ++ stdout_read_guard.reset(-1); ++ stderr_read_guard.reset(-1); + exit(EXIT_FAILURE); + } +  +@@ -312,13 +416,17 @@ int execute( + write_all(STDERR_FILENO, buf, byte_count); + } else { + // this should never happen +- perror("Logic error!"); ++ logger.log(E_FATAL, "Logic error: unexpected pipe index."); + exit(EXIT_FAILURE); + } + } + } + if (watched_fds[this_fd].revents & POLLERR) { +- close(watched_fds[this_fd].fd); ++ if (this_fd == CHILD_PIPE_NAMES::STDOUT_READ) { ++ stdout_read_guard.reset(-1); ++ } else if (this_fd == CHILD_PIPE_NAMES::STDERR_READ) { ++ stderr_read_guard.reset(-1); ++ } + break_out = true; + } + if (watched_fds[this_fd].revents & POLLHUP) { +@@ -333,21 +441,46 @@ int execute( + waitpid(pid, &status, 0); +  + // Drain the pipes before exiting +- while ((byte_count = read(fd_child_stdout_pipe[READ_END], buf, BUFFER_SIZE)) > 0) { ++ while ((byte_count = read(stdout_read_guard.get(), buf, BUFFER_SIZE)) > 0) { + write_all(stdout_log_fh->_fileno, buf, byte_count); + write_all(STDOUT_FILENO, buf, byte_count); + } +- while ((byte_count = read(fd_child_stderr_pipe[READ_END], buf, BUFFER_SIZE)) > 0) { ++ while ((byte_count = read(stderr_read_guard.get(), buf, BUFFER_SIZE)) > 0) { + write_all(stderr_log_fh->_fileno, buf, byte_count); + write_all(STDERR_FILENO, buf, byte_count); + } +  +- if WIFEXITED(status) { +- return WEXITSTATUS(status); +- } else { +- return -617; ++ int status; ++ pid_t pid = waitpid(-1, &status, WNOHANG); // Non-blocking wait for child process ++ if (pid > 0) { // If a child process has terminated ++ // Check if the child process exited normally ++ if (WIFEXITED(status)) { ++ int exit_code = WEXITSTATUS(status); ++ logger.log(E_INFO, "Child exited with status " + std::to_string(exit_code)); ++ logger.log_to_json_file("E_INFO", "Child exited with status " + std::to_string(exit_code), user, group, command); ++ } ++ // Check if the child process was terminated by a signal ++ else if (WIFSIGNALED(status)) { ++ int signal_number = WTERMSIG(status); ++ logger.log(E_FATAL, "Process terminated by signal: " + std::to_string(signal_number)); ++ logger.log_to_json_file("E_FATAL", "Process terminated by signal: " + std::to_string(signal_number), user, group, command); ++ // Reset signal handler to default before exiting ++ signal(signal_number, SIG_DFL); ++ // Return 128 + signal number as the exit code for signal termination ++ return 128 + signal_number; // POSIX exit code format ++ } ++ // Handle unexpected exit conditions ++ else { ++ logger.log(E_WARN, "Unknown child exit condition."); ++ logger.log_to_json_file("E_WARN", "Unknown child exit condition", user, group, command); ++ } + } + } + } ++ return 0; ++ } catch (const std::exception& ex) { ++ std::cerr << "[LCPEX ERROR] " << ex.what() << std::endl; ++ return -999; ++} + } +