From 08c9d5bf07e17b8028864ef0f0957a4b31af82fb Mon Sep 17 00:00:00 2001 From: Anilkumar18 Date: Mon, 4 Aug 2025 15:20:11 +0530 Subject: [PATCH 1/2] lcpex enhancements - 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 | 150 ++++++++++++++++++- src/logger/Logger.h | 33 ++++- 6 files changed, 597 insertions(+), 103 deletions(-) create mode 100644 src/lcpex/FileHandle.h 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..5fe2dfd 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,138 @@ 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); + } } 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 -- 2.43.0 From bd8191c2d25b3ba2b4d8e0abdf2b61fb147f33fa Mon Sep 17 00:00:00 2001 From: Anilkumar18 Date: Mon, 4 Aug 2025 15:53:41 +0530 Subject: [PATCH 2/2] lcpex enhancements - modified json log file format --- src/lcpex/liblcpex.cpp | 94 ++++++++++++++++++++------------- src/lcpex/liblcpex.h | 8 ++- src/lcpex/vpty/libclpex_tty.cpp | 85 ++++++++++++++++------------- src/lcpex/vpty/libclpex_tty.h | 4 +- src/logger/Logger.cpp | 22 ++++++-- src/logger/Logger.h | 3 +- src/plan/Task.cpp | 12 +++-- 7 files changed, 141 insertions(+), 87 deletions(-) diff --git a/src/lcpex/liblcpex.cpp b/src/lcpex/liblcpex.cpp index db79c19..dc88d49 100644 --- a/src/lcpex/liblcpex.cpp +++ b/src/lcpex/liblcpex.cpp @@ -13,6 +13,10 @@ static Logger logger(E_INFO, "lcpex"); std::string user = logger.get_user_name(); std::string group = logger.get_group_name(); +// Global variables for signal handler context +static std::string g_task_context = ""; +static std::string g_log_directory = ""; + void signal_handler(int sig) { std::string command = "signal_handler"; // Command context @@ -20,20 +24,24 @@ void signal_handler(int sig) { // 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); + logger.log_to_json_file("E_INFO", "SIGCHLD received. A child process ended.", user, group, command, g_task_context, g_log_directory); } else if (sig == SIGINT) { - logger.log_to_json_file("E_FATAL", "SIGINT received. Gracefully terminating...", user, group, command); + logger.log_to_json_file("E_FATAL", "SIGINT received. Gracefully terminating...", user, group, command, g_task_context, g_log_directory); } else if (sig == SIGTERM) { - logger.log_to_json_file("E_FATAL", "SIGTERM received. Terminating...", user, group, command); + logger.log_to_json_file("E_FATAL", "SIGTERM received. Terminating...", user, group, command, g_task_context, g_log_directory); } else if (sig == SIGSEGV) { - logger.log_to_json_file("E_FATAL", "SIGSEGV received. Possible segmentation fault.", user, group, command); + logger.log_to_json_file("E_FATAL", "SIGSEGV received. Possible segmentation fault.", user, group, command, g_task_context, g_log_directory); } else { - logger.log_to_json_file("E_FATAL", "Unhandled signal received", user, group, command); + logger.log_to_json_file("E_FATAL", "Unhandled signal received", user, group, command, g_task_context, g_log_directory); } } // Setup signal registrations -void setup_signal_handlers() { +void setup_signal_handlers(std::string task_context = "", std::string log_directory = "") { + // Set global variables for signal handler context + g_task_context = task_context; + g_log_directory = log_directory; + struct sigaction sa; sa.sa_handler = signal_handler; // <-- handler function sigemptyset(&sa.sa_mask); @@ -43,25 +51,25 @@ void setup_signal_handlers() { // 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 + logger.log_to_json_file("E_FATAL", error_message, user, group, command, task_context, log_directory); // 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 + logger.log_to_json_file("E_FATAL", error_message, user, group, command, task_context, log_directory); // 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 + logger.log_to_json_file("E_FATAL", error_message, user, group, command, task_context, log_directory); // 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 + logger.log_to_json_file("E_FATAL", error_message, user, group, command, task_context, log_directory); // Log to JSON file } } @@ -72,7 +80,9 @@ std::string prefix_generator( std::string shell_execution_arg, bool supply_environment, std::string shell_source_subcommand, - std::string environment_file_path + std::string environment_file_path, + std::string task_context, + std::string log_directory ) { std::string prefix = ""; if ( is_shell_command ) { @@ -103,7 +113,7 @@ std::string prefix_generator( } // Log the message to JSON file - logger.log_to_json_file("E_INFO", "LAUNCHER: " + prefix, user, group, command); + logger.log_to_json_file("E_INFO", "LAUNCHER: " + prefix, user, group, command, task_context, log_directory); //logger.log(E_INFO, "LAUNCHER: " + prefix); return prefix; @@ -123,7 +133,9 @@ int lcpex( std::string shell_execution_arg, bool supply_environment, std::string shell_source_subcommand, - std::string environment_file_path + std::string environment_file_path, + std::string task_context, + std::string log_directory ) { // generate the prefix @@ -134,19 +146,21 @@ int lcpex( shell_execution_arg, supply_environment, shell_source_subcommand, - environment_file_path + environment_file_path, + task_context, + log_directory ); command = prefix; - setup_signal_handlers(); + setup_signal_handlers(task_context, log_directory); // if we are forcing a pty, then we will use the vpty library if( force_pty ) { - return exec_pty( command, stdout_log_fh, stderr_log_fh, context_override, context_user, context_group, supply_environment ); + return exec_pty( command, stdout_log_fh, stderr_log_fh, context_override, context_user, context_group, supply_environment, task_context, log_directory ); } // otherwise, we will use the execute function - return execute( command, stdout_log_fh, stderr_log_fh, context_override, context_user, context_group, supply_environment ); + return execute( command, stdout_log_fh, stderr_log_fh, context_override, context_user, context_group, supply_environment, task_context, log_directory ); } /** @@ -158,14 +172,14 @@ int lcpex( * * @param fd The file descriptor for which to set the close-on-exec flag */ -void set_cloexec_flag(int fd) +void set_cloexec_flag(int fd, std::string task_context = "", std::string log_directory = "") { std::string command = "set_cloexec_flag"; if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -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); + logger.log_to_json_file("E_FATAL", error_message, user, group, command, task_context, log_directory); throw std::runtime_error("fcntl(F_SETFD) failed for fd " + std::to_string(fd)); } @@ -195,7 +209,7 @@ void set_cloexec_flag(int fd) * Finally, the child process calls execvp() with the processed_command to run the shell command. * If the execvp() function fails, an error message is displayed. */ -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[]) { +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[], std::string task_context = "", std::string log_directory = "") { 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 @@ -213,33 +227,33 @@ void run_child_process(bool context_override, const char* context_user, const ch break; case IDENTITY_CONTEXT_ERRORS::ERROR_NO_SUCH_USER: 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); + logger.log_to_json_file("E_FATAL", "Aborting: context user not found: " + std::string(context_user), user, group, command, task_context, log_directory); _exit(1); break; case IDENTITY_CONTEXT_ERRORS::ERROR_NO_SUCH_GROUP: 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); + logger.log_to_json_file("E_FATAL", "Aborting: context group not found: " + std::string(context_group), user, group, command, task_context, log_directory); _exit(1); break; case IDENTITY_CONTEXT_ERRORS::ERROR_SETGID_FAILED: 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); + logger.log_to_json_file("E_FATAL", "Aborting: Setting GID failed: " + std::string(context_user) + "/" + std::string(context_group), user, group, command, task_context, log_directory); _exit(1); break; case IDENTITY_CONTEXT_ERRORS::ERROR_SETUID_FAILED: 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); + logger.log_to_json_file("E_FATAL", "Aborting: Setting UID failed: " + std::string(context_user) + "/" + std::string(context_group), user, group, command, task_context, log_directory); _exit(1); break; default: 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); + logger.log_to_json_file("E_FATAL", "Aborting: Unknown error while setting identity context.", user, group, command, task_context, log_directory); _exit(1); break; } } int exit_code = execvp(processed_command[0], processed_command); - logger.log_to_json_file("E_FATAL", "failed on execvp in child", user, group, command); + logger.log_to_json_file("E_FATAL", "failed on execvp in child", user, group, command, task_context, log_directory); logger.log(E_FATAL, "failed on execvp in child"); _Exit(exit_code); } @@ -251,7 +265,9 @@ int execute( bool context_override, std::string context_user, std::string context_group, - bool environment_supplied + bool environment_supplied, + std::string task_context, + std::string log_directory ){ try { @@ -286,10 +302,10 @@ int execute( } // using O_CLOEXEC to ensure that the child process closes the file descriptors - set_cloexec_flag( fd_child_stdout_pipe[READ_END] ); - set_cloexec_flag( fd_child_stderr_pipe[READ_END] ); - set_cloexec_flag( fd_child_stdout_pipe[WRITE_END] ); - set_cloexec_flag( fd_child_stderr_pipe[WRITE_END] ); + set_cloexec_flag( fd_child_stdout_pipe[READ_END], task_context, log_directory ); + set_cloexec_flag( fd_child_stderr_pipe[READ_END], task_context, log_directory ); + set_cloexec_flag( fd_child_stdout_pipe[WRITE_END], task_context, log_directory ); + set_cloexec_flag( fd_child_stderr_pipe[WRITE_END], task_context, log_directory ); // status result basket for the parent process to capture the child's exit status int status; @@ -300,7 +316,7 @@ int execute( { // fork failed 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); + logger.log_to_json_file("E_FATAL", "fork failure: " + std::string(strerror(errno)), user, group, command, task_context, log_directory); 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 @@ -317,7 +333,9 @@ int execute( context_group.c_str(), processed_command, fd_child_stdout_pipe, - fd_child_stderr_pipe + fd_child_stderr_pipe, + task_context, + log_directory ); } @@ -376,7 +394,7 @@ int execute( if (num_files_readable == -1) { // error occurred in poll() logger.log(E_FATAL, "poll() failed: " + std::string(strerror(errno))); - logger.log_to_json_file("E_FATAL", "poll() failed", user, group, command); + logger.log_to_json_file("E_FATAL", "poll() failed", user, group, command, task_context, log_directory); throw std::runtime_error("poll() failed"); } if (num_files_readable == 0) { @@ -393,7 +411,7 @@ int execute( if (errno == EAGAIN) { continue; } else { // error reading from pipe 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); + logger.log_to_json_file("E_FATAL", "Error while reading from pipe", user, group, command, task_context, log_directory); stdout_read_guard.reset(-1); stderr_read_guard.reset(-1); exit(EXIT_FAILURE); @@ -457,13 +475,13 @@ int execute( 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); + logger.log_to_json_file("E_INFO", "Child exited with status " + std::to_string(exit_code), user, group, command, task_context, log_directory); } // 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); + logger.log_to_json_file("E_FATAL", "Process terminated by signal: " + std::to_string(signal_number), user, group, command, task_context, log_directory); // Reset signal handler to default before exiting signal(signal_number, SIG_DFL); // Return 128 + signal number as the exit code for signal termination @@ -472,7 +490,7 @@ int execute( // 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); + logger.log_to_json_file("E_WARN", "Unknown child exit condition", user, group, command, task_context, log_directory); } } } diff --git a/src/lcpex/liblcpex.h b/src/lcpex/liblcpex.h index f958661..7e7d147 100644 --- a/src/lcpex/liblcpex.h +++ b/src/lcpex/liblcpex.h @@ -44,7 +44,9 @@ int execute( bool context_override, std::string context_user, std::string context_group, - bool environment_supplied + bool environment_supplied, + std::string task_context = "", + std::string log_directory = "" ); @@ -87,7 +89,9 @@ int lcpex( std::string shell_execution_arg, bool supply_environment, std::string shell_source_subcommand, - std::string environment_file_path + std::string environment_file_path, + std::string task_context = "", + std::string log_directory = "" ); /** diff --git a/src/lcpex/vpty/libclpex_tty.cpp b/src/lcpex/vpty/libclpex_tty.cpp index 72f47bf..f1b3fd0 100644 --- a/src/lcpex/vpty/libclpex_tty.cpp +++ b/src/lcpex/vpty/libclpex_tty.cpp @@ -12,6 +12,9 @@ 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(); +// Global variables for signal handler context +static std::string g_tty_task_context = ""; +static std::string g_tty_log_directory = ""; void tty_signal_handler(int sig) { std::string command = "tty_signal_handler"; // Command context @@ -19,19 +22,23 @@ void tty_signal_handler(int sig) { // 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); + tty_logger.log_to_json_file("E_INFO", "SIGCHLD received. A child process ended.", tty_user, tty_group, command, g_tty_task_context, g_tty_log_directory); } else if (sig == SIGINT) { - tty_logger.log_to_json_file("E_FATAL", "SIGINT received. Gracefully terminating...", tty_user, tty_group, command); + tty_logger.log_to_json_file("E_FATAL", "SIGINT received. Gracefully terminating...", tty_user, tty_group, command, g_tty_task_context, g_tty_log_directory); } else if (sig == SIGTERM) { - tty_logger.log_to_json_file("E_FATAL", "SIGTERM received. Terminating...", tty_user, tty_group, command); + tty_logger.log_to_json_file("E_FATAL", "SIGTERM received. Terminating...", tty_user, tty_group, command, g_tty_task_context, g_tty_log_directory); } else if (sig == SIGSEGV) { - tty_logger.log_to_json_file("E_FATAL", "SIGSEGV received. Possible segmentation fault.", tty_user, tty_group, command); + tty_logger.log_to_json_file("E_FATAL", "SIGSEGV received. Possible segmentation fault.", tty_user, tty_group, command, g_tty_task_context, g_tty_log_directory); } else { - tty_logger.log_to_json_file("E_FATAL", "Unhandled signal received", tty_user, tty_group, command); + tty_logger.log_to_json_file("E_FATAL", "Unhandled signal received", tty_user, tty_group, command, g_tty_task_context, g_tty_log_directory); } } // Setup signal registrations -void setup_tty_signal_handlers() { +void setup_tty_signal_handlers(std::string task_context = "", std::string log_directory = "") { + // Set global variables for signal handler context + g_tty_task_context = task_context; + g_tty_log_directory = log_directory; + struct sigaction sa; sa.sa_handler = tty_signal_handler; // <-- handler function sigemptyset(&sa.sa_mask); @@ -41,25 +48,25 @@ void setup_tty_signal_handlers() { // 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 + tty_logger.log_to_json_file("E_FATAL", error_message, tty_user, tty_group, command, task_context, log_directory); // 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 + tty_logger.log_to_json_file("E_FATAL", error_message, tty_user, tty_group, command, task_context, log_directory); // 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 + tty_logger.log_to_json_file("E_FATAL", error_message, tty_user, tty_group, command, task_context, log_directory); // 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 + tty_logger.log_to_json_file("E_FATAL", error_message, tty_user, tty_group, command, task_context, log_directory); // Log to JSON file } } @@ -75,21 +82,21 @@ void setup_tty_signal_handlers() { * Finally, the function exits the program with exit code 1. */ -void set_tty_cloexec_flag(int fd) +void set_tty_cloexec_flag(int fd, std::string task_context = "", std::string log_directory = "") { 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); + tty_logger.log_to_json_file("E_FATAL", error_message, tty_user, tty_group, command, task_context, log_directory); throw std::runtime_error("fcntl(F_SETFD) failed for fd " + std::to_string(fd)); } } -void safe_perror( const char * msg, struct termios * ttyOrig ) +void safe_perror( const char * msg, struct termios * ttyOrig, std::string task_context = "", std::string log_directory = "" ) { 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 + tty_logger.log_to_json_file("E_FATAL", msg, tty_user, tty_group, command, task_context, log_directory); // Log the message before exiting std::cerr << msg << std::endl; ttyResetExit( ttyOrig ); throw std::runtime_error(std::string("TTY Error: ") + msg); @@ -118,7 +125,7 @@ void safe_perror( const char * msg, struct termios * ttyOrig ) * Finally, the function executes the command specified in `processed_command` using `execvp()`. * If the execution of `execvp()` fails, the function calls `safe_perror()` to print a message and exit the program. */ -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 ) +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 task_context = "", std::string log_directory = "" ) { std::string command = "run_child_process_tty"; // Command context // redirect stderr to the write end of the stderr pipe @@ -138,27 +145,27 @@ void run_child_process( int fd_child_stderr_pipe[2], char * processed_command[], break; case IDENTITY_CONTEXT_ERRORS::ERROR_NO_SUCH_USER: 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); + tty_logger.log_to_json_file("E_FATAL", "Context user not found: " + context_user, tty_user, tty_group, command, task_context, log_directory); _exit(1); break; case IDENTITY_CONTEXT_ERRORS::ERROR_NO_SUCH_GROUP: 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); + tty_logger.log_to_json_file("E_FATAL", "Context group not found: " + context_group, tty_user, tty_group, command, task_context, log_directory); _exit(1); break; case IDENTITY_CONTEXT_ERRORS::ERROR_SETGID_FAILED: 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); + tty_logger.log_to_json_file("E_FATAL", "Setting GID failed for: " + context_user + "/" + context_group, tty_user, tty_group, command, task_context, log_directory); _exit(1); break; case IDENTITY_CONTEXT_ERRORS::ERROR_SETUID_FAILED: 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); + tty_logger.log_to_json_file("E_FATAL", "Setting UID failed for: " + context_user + "/" + context_group, tty_user, tty_group, command, task_context, log_directory); _exit(1); break; default: 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); + tty_logger.log_to_json_file("E_FATAL", "Unknown error while setting identity context.", tty_user, tty_group, command, task_context, log_directory); _exit(1); break; } @@ -167,7 +174,7 @@ 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 ); - tty_logger.log_to_json_file("E_FATAL", "failed on execvp in child", tty_user, tty_group, command); + tty_logger.log_to_json_file("E_FATAL", "failed on execvp in child", tty_user, tty_group, command, task_context, log_directory); tty_logger.log(E_FATAL, "failed on execvp in child"); _Exit(exit_code); @@ -184,13 +191,15 @@ int exec_pty( bool context_override, std::string context_user, std::string context_group, - bool environment_supplied + bool environment_supplied, + std::string task_context, + std::string log_directory ) { 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); + tty_logger.log_to_json_file("E_INFO", "TTY LAUNCHER: " + command, tty_user, tty_group, exec_command, task_context, log_directory); // Setup signal handlers - setup_tty_signal_handlers(); + setup_tty_signal_handlers(task_context, log_directory); // initialize the terminal settings obj struct termios ttyOrig; @@ -205,12 +214,12 @@ int exec_pty( char ** processed_command = expand_env( command ); if ( stdout_log_fh == NULL ) { - tty_logger.log_to_json_file("E_FATAL", "Error opening STDOUT log file. Aborting.", tty_user, tty_group, exec_command); + tty_logger.log_to_json_file("E_FATAL", "Error opening STDOUT log file. Aborting.", tty_user, tty_group, exec_command, task_context, log_directory); throw std::runtime_error("Error opening STDOUT log file"); } if ( stderr_log_fh == NULL ) { - tty_logger.log_to_json_file("E_FATAL", "Error opening STDERR log file. Aborting.", tty_user, tty_group, exec_command); + tty_logger.log_to_json_file("E_FATAL", "Error opening STDERR log file. Aborting.", tty_user, tty_group, exec_command, task_context, log_directory); throw std::runtime_error("Error opening STDERR log file"); } @@ -220,12 +229,12 @@ int exec_pty( if ( pipe( fd_child_stderr_pipe ) == -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); + tty_logger.log_to_json_file("E_FATAL", error_message, tty_user, tty_group, exec_command, task_context, log_directory); throw std::runtime_error("Failed to create stderr pipe"); } // 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] ); + set_tty_cloexec_flag( fd_child_stderr_pipe[READ_END], task_context, log_directory ); + set_tty_cloexec_flag( fd_child_stderr_pipe[WRITE_END], task_context, log_directory ); // status result basket for the parent process to capture the child's exit status int status = 616; @@ -236,14 +245,14 @@ int exec_pty( /* Retrieve the attributes of terminal on which we are started */ if (tcgetattr(STDIN_FILENO, &ttyOrig) == -1){ - tty_logger.log_to_json_file("E_FATAL", "tcgetattr failed", tty_user, tty_group, exec_command); + tty_logger.log_to_json_file("E_FATAL", "tcgetattr failed", tty_user, tty_group, exec_command, task_context, log_directory); 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) { - tty_logger.log_to_json_file("E_FATAL", "ioctl-TIOCGWINSZ failed", tty_user, tty_group, exec_command); + tty_logger.log_to_json_file("E_FATAL", "ioctl-TIOCGWINSZ failed", tty_user, tty_group, exec_command, task_context, log_directory); 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"); @@ -254,7 +263,7 @@ int exec_pty( { // fork failed 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); + tty_logger.log_to_json_file("E_FATAL", "ptyfork failure: " + std::string(strerror(errno)), tty_user, tty_group, exec_command, task_context, log_directory); 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))); @@ -262,7 +271,7 @@ int exec_pty( case 0: { // child process - run_child_process( fd_child_stderr_pipe, processed_command, &ttyOrig, context_override, context_user, context_group ); + run_child_process( fd_child_stderr_pipe, processed_command, &ttyOrig, context_override, context_user, context_group, task_context, log_directory ); } default: { @@ -320,7 +329,7 @@ int exec_pty( if (num_files_readable == -1) { // error occurred in poll() 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); + tty_logger.log_to_json_file("E_FATAL", "poll() failed", tty_user, tty_group, exec_command, task_context, log_directory); ttyResetExit( &ttyOrig); throw std::runtime_error("poll() failed"); } @@ -340,7 +349,7 @@ int exec_pty( if (errno == EAGAIN) { continue; } else { // error reading from pipe 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); + tty_logger.log_to_json_file("E_FATAL", "Error while reading from pipe", tty_user, tty_group, exec_command, task_context, log_directory); ttyResetExit( &ttyOrig); stderr_read_guard.reset(-1); master_fd_guard.reset(-1); @@ -415,7 +424,7 @@ int exec_pty( 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); + tty_logger.log_to_json_file("E_INFO", "Child exited with status " + std::to_string(exit_code), tty_user, tty_group, exec_command, task_context, log_directory); ttyResetExit( &ttyOrig); return exit_code; } @@ -423,7 +432,7 @@ int exec_pty( 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); + tty_logger.log_to_json_file("E_FATAL", "Process terminated by signal: " + std::to_string(signal_number), tty_user, tty_group, exec_command, task_context, log_directory); // Reset signal handler to default before exiting signal(signal_number, SIG_DFL); ttyResetExit( &ttyOrig); @@ -432,7 +441,7 @@ int exec_pty( // 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); + tty_logger.log_to_json_file("E_WARN", "Unknown child exit condition", tty_user, tty_group, exec_command, task_context, log_directory); } } diff --git a/src/lcpex/vpty/libclpex_tty.h b/src/lcpex/vpty/libclpex_tty.h index 328cf8f..407d165 100644 --- a/src/lcpex/vpty/libclpex_tty.h +++ b/src/lcpex/vpty/libclpex_tty.h @@ -43,7 +43,9 @@ int exec_pty( bool context_override, std::string context_user, std::string context_group, - bool environment_supplied + bool environment_supplied, + std::string task_context = "", + std::string log_directory = "" ); diff --git a/src/logger/Logger.cpp b/src/logger/Logger.cpp index 5fe2dfd..37518ff 100644 --- a/src/logger/Logger.cpp +++ b/src/logger/Logger.cpp @@ -129,7 +129,8 @@ std::string Logger::get_current_timestamp() //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) + const std::string& command, const std::string& task_context, + const std::string& log_directory, bool log_to_console) { // Log to console if requested const char* log_level_str = "UNKNOWN"; @@ -148,9 +149,22 @@ void Logger::log_to_json_file(const std::string& log_level, const std::string& m 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"; + // 3. Generate filename with format: YYYY-MM-DD_context.log.json + std::string filename; + if (!task_context.empty() && !log_directory.empty()) { + // Use task-specific directory and format + time_t now = time(NULL); + struct tm* timeinfo = localtime(&now); + char date_buffer[16]; + strftime(date_buffer, sizeof(date_buffer), "%Y-%m-%d", timeinfo); + std::string date_str = std::string(date_buffer); + + filename = log_directory + "/" + task_context + "/" + date_str + "_" + task_context + ".log.json"; + } else { + // Fallback to original format for backward compatibility + std::string timestamp = get_current_timestamp(); + 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)); diff --git a/src/logger/Logger.h b/src/logger/Logger.h index ea79b5f..5e3af7d 100644 --- a/src/logger/Logger.h +++ b/src/logger/Logger.h @@ -51,7 +51,8 @@ class Logger { 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); + const std::string& command, const std::string& task_context = "", + const std::string& log_directory = "", bool log_to_console = true); // Helper methods std::string get_current_timestamp(); std::string get_user_name(); diff --git a/src/plan/Task.cpp b/src/plan/Task.cpp index 6263388..e96d8ad 100644 --- a/src/plan/Task.cpp +++ b/src/plan/Task.cpp @@ -470,7 +470,9 @@ void Task::execute( Conf * configuration ) shell_definition.execution_arg, supply_environment, shell_definition.source_cmd, - environment_file + environment_file, + task_name, + logs_root ); // ********************************************** @@ -539,7 +541,9 @@ void Task::execute( Conf * configuration ) shell_definition.execution_arg, supply_environment, shell_definition.source_cmd, - environment_file + environment_file, + task_name, + logs_root ); // ********************************************** @@ -591,7 +595,9 @@ void Task::execute( Conf * configuration ) shell_definition.execution_arg, supply_environment, shell_definition.source_cmd, - environment_file + environment_file, + task_name, + logs_root ); // ********************************************** -- 2.43.0