minecraft-pi-reborn/libreborn/src/util/exec.c

213 lines
6.0 KiB
C
Raw Normal View History

2022-05-14 02:36:12 +00:00
#include <pthread.h>
2022-03-14 23:09:25 +00:00
#include <libreborn/exec.h>
2022-07-20 06:58:14 +00:00
// Set Environmental Variable
static void setenv_safe(const char *name, const char *value) {
if (value != NULL) {
setenv(name, value, 1);
} else {
unsetenv(name);
}
}
void set_and_print_env(const char *name, const char *value) {
// Set The Value
setenv_safe(name, value);
// Print New Value
DEBUG("Set %s = %s", name, value != NULL ? value : "(unset)");
2022-07-20 06:58:14 +00:00
}
2022-03-14 23:09:25 +00:00
// Safe execvpe()
2022-07-20 06:58:14 +00:00
#define handle_environmental_variable(var) \
{ \
const char *full_var = is_arm_component ? "MCPI_ARM_" var : "MCPI_NATIVE_" var; \
const char *var_value = getenv(full_var); \
set_and_print_env(var, var_value); \
}
void setup_exec_environment(int is_arm_component) {
for_each_special_environmental_variable(handle_environmental_variable);
}
2022-03-14 23:09:25 +00:00
__attribute__((noreturn)) void safe_execvpe(const char *const argv[], const char *const envp[]) {
2022-07-02 22:14:23 +00:00
// Log
DEBUG("Running Command:");
for (int i = 0; argv[i] != NULL; i++) {
DEBUG(" %s", argv[i]);
}
// Run
2022-03-14 23:09:25 +00:00
int ret = execvpe(argv[0], (char *const *) argv, (char *const *) envp);
if (ret == -1) {
ERR("Unable To Execute Program: %s: %s", argv[0], strerror(errno));
} else {
IMPOSSIBLE();
}
}
// Chop Off Last Component
void chop_last_component(char **str) {
size_t length = strlen(*str);
for (size_t i = 0; i < length; i++) {
size_t j = length - i - 1;
if ((*str)[j] == '/') {
(*str)[j] = '\0';
break;
}
}
}
// Get Binary Directory (Remember To Free)
char *get_binary_directory() {
// Get Path To Current Executable
char *exe = realpath("/proc/self/exe", NULL);
ALLOC_CHECK(exe);
// Chop Off Last Component
chop_last_component(&exe);
// Return
return exe;
}
// Run Command And Get Output
2022-05-15 17:51:28 +00:00
char *run_command(const char *const command[], int *exit_status) {
2022-03-14 23:09:25 +00:00
// Store Output
int output_pipe[2];
safe_pipe2(output_pipe, 0);
// Run
pid_t ret = fork();
if (ret == -1) {
ERR("Unable To Run Command: %s", strerror(errno));
} else if (ret == 0) {
// Child Process
2022-09-22 21:43:21 +00:00
// Set Debug Tag
reborn_debug_tag = CHILD_PROCESS_TAG;
2022-03-14 23:09:25 +00:00
// Pipe stdout
dup2(output_pipe[1], STDOUT_FILENO);
close(output_pipe[0]);
close(output_pipe[1]);
2022-10-02 04:47:11 +00:00
// Setup stderr
if (getenv("MCPI_DEBUG") == NULL) {
const char *log_file_fd_env = getenv("MCPI_LOG_FILE_FD");
if (log_file_fd_env == NULL) {
IMPOSSIBLE();
}
dup2(atoi(log_file_fd_env), STDERR_FILENO);
}
2022-07-20 06:58:14 +00:00
// Setup Environment
setup_exec_environment(0);
2022-03-14 23:09:25 +00:00
// Run
safe_execvpe(command, (const char *const *) environ);
} else {
// Parent Process
2022-05-14 02:36:12 +00:00
track_child(ret);
2022-03-14 23:09:25 +00:00
// Read stdout
close(output_pipe[1]);
2022-03-16 23:51:33 +00:00
#define BUFFER_SIZE 1024
2023-10-19 05:23:34 +00:00
size_t size = BUFFER_SIZE;
char *output = malloc(size);
2022-03-16 23:51:33 +00:00
char buf[BUFFER_SIZE];
2023-10-19 05:23:34 +00:00
size_t position = 0;
2022-05-14 02:36:12 +00:00
ssize_t bytes_read = 0;
2023-10-19 05:23:34 +00:00
while ((bytes_read = read(output_pipe[0], (void *) buf, BUFFER_SIZE)) > 0) {
// Grow Output If Needed
size_t needed_size = position + bytes_read;
if (needed_size > size) {
// More Memeory Needed
size_t new_size = size;
while (new_size < needed_size) {
new_size += BUFFER_SIZE;
}
char *new_output = realloc(output, new_size);
if (new_output == NULL) {
ERR("Unable To Grow Output Buffer");
} else {
output = new_output;
size = new_size;
}
}
// Copy Data To Output
memcpy(output + position, buf, bytes_read);
position += bytes_read;
2022-03-14 23:09:25 +00:00
}
close(output_pipe[0]);
2023-10-19 05:23:34 +00:00
// Add NULL-Terminator To Output
size_t needed_size = position + 1;
if (needed_size > size) {
// More Memeory Needed
size_t new_size = size + 1;
char *new_output = realloc(output, new_size);
if (new_output == NULL) {
ERR("Unable To Grow Output Buffer (For NULL-Terminator)");
} else {
output = new_output;
}
}
output[position] = '\0';
2022-03-14 23:09:25 +00:00
// Get Return Code
int status;
waitpid(ret, &status, 0);
2022-05-14 02:36:12 +00:00
untrack_child(ret);
2022-05-15 17:51:28 +00:00
if (exit_status != NULL) {
*exit_status = status;
2022-05-03 02:44:10 +00:00
}
2022-03-14 23:09:25 +00:00
// Return
return output;
}
}
2022-05-14 02:36:12 +00:00
2022-05-15 17:51:28 +00:00
// Get Exit Status String
void get_exit_status_string(int status, char **out) {
if (out != NULL) {
*out =NULL;
if (WIFEXITED(status)) {
safe_asprintf(out, ": Exit Code: %i", WEXITSTATUS(status));
} else if (WIFSIGNALED(status)) {
safe_asprintf(out, ": Signal: %i%s", WTERMSIG(status), WCOREDUMP(status) ? " (Core Dumped)" : "");
} else {
safe_asprintf(out, ": Terminated");
}
}
}
2022-05-14 02:36:12 +00:00
// Track Children
#define MAX_CHILDREN 128
static pid_t children[MAX_CHILDREN] = { 0 };
static pthread_mutex_t children_lock = PTHREAD_MUTEX_INITIALIZER;
void track_child(pid_t pid) {
pthread_mutex_lock(&children_lock);
for (int i = 0; i < MAX_CHILDREN; i++) {
if (children[i] == 0) {
children[i] = pid;
break;
}
}
pthread_mutex_unlock(&children_lock);
}
void untrack_child(pid_t pid) {
pthread_mutex_lock(&children_lock);
for (int i = 0; i < MAX_CHILDREN; i++) {
if (children[i] == pid) {
children[i] = 0;
}
}
pthread_mutex_unlock(&children_lock);
}
void murder_children() {
pthread_mutex_lock(&children_lock);
for (int i = 0; i < MAX_CHILDREN; i++) {
if (children[i] != 0) {
kill(children[i], SIGTERM);
}
}
pthread_mutex_unlock(&children_lock);
}