rex/tatus

371 lines
20 KiB
Plaintext
Raw Blame History

This file contains invisible Unicode characters!

This file contains invisible Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden characters.

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 <cstring>
+#include <stdexcept>
+#include <signal.h>
+#include <string>
+#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;
+}
}