2024-02-15 19:42:22 +01:00
|
|
|
#include "utils.h"
|
|
|
|
|
|
|
|
#include <stdarg.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <time.h>
|
|
|
|
#include <string.h>
|
2024-02-17 23:59:38 +01:00
|
|
|
#include <stdlib.h>
|
2024-02-15 19:42:22 +01:00
|
|
|
#include <errno.h>
|
2024-02-17 01:20:20 +01:00
|
|
|
#include <unistd.h>
|
2024-02-17 12:34:24 +01:00
|
|
|
#include <fcntl.h>
|
2024-02-17 01:20:20 +01:00
|
|
|
#include <sys/wait.h>
|
2024-02-17 23:59:38 +01:00
|
|
|
#include <sys/types.h>
|
2024-02-15 19:42:22 +01:00
|
|
|
|
2024-02-17 23:59:38 +01:00
|
|
|
LogLevel log_level = LOG_LEVEL_INFO;
|
2024-02-15 19:42:22 +01:00
|
|
|
|
2024-02-17 23:59:38 +01:00
|
|
|
void SetLogLevel(LogLevel level) {
|
2024-02-17 00:40:09 +01:00
|
|
|
log_level = level;
|
2024-02-15 19:42:22 +01:00
|
|
|
}
|
|
|
|
|
2024-02-17 23:59:38 +01:00
|
|
|
void Log(LogLevel level, const char* format, ...) {
|
2024-02-17 00:40:09 +01:00
|
|
|
if (level < log_level)
|
|
|
|
return;
|
|
|
|
|
2024-02-15 19:42:22 +01:00
|
|
|
va_list args;
|
2024-02-17 00:40:09 +01:00
|
|
|
va_start(args, format);
|
2024-02-15 19:42:22 +01:00
|
|
|
|
|
|
|
const char* color;
|
|
|
|
const char* level_str;
|
2024-02-17 00:40:09 +01:00
|
|
|
// Set the color and level_str based on the log level
|
2024-02-15 19:42:22 +01:00
|
|
|
switch (level) {
|
2024-02-17 00:40:09 +01:00
|
|
|
case LOG_LEVEL_DEBUG:
|
2024-02-15 19:42:22 +01:00
|
|
|
color = "\033[0;90m";
|
|
|
|
level_str = "DEBUG";
|
|
|
|
break;
|
2024-02-17 23:59:38 +01:00
|
|
|
case LOG_LEVEL_INFO:
|
|
|
|
color = "\033[0;36m";
|
|
|
|
level_str = "INFO";
|
2024-02-17 00:40:09 +01:00
|
|
|
case LOG_LEVEL_WARNING:
|
2024-02-15 19:42:22 +01:00
|
|
|
color = "\033[0;33m";
|
2024-02-17 00:40:09 +01:00
|
|
|
level_str = "WARNING";
|
2024-02-15 19:42:22 +01:00
|
|
|
break;
|
2024-02-17 00:40:09 +01:00
|
|
|
case LOG_LEVEL_ERROR:
|
2024-02-15 19:42:22 +01:00
|
|
|
color = "\033[0;31m";
|
|
|
|
level_str = "ERROR";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get the current time
|
|
|
|
time_t t = time(NULL);
|
|
|
|
struct tm* tm_info = localtime(&t);
|
|
|
|
|
2024-02-17 00:40:09 +01:00
|
|
|
// Print the label, message and newline
|
2024-02-15 19:42:22 +01:00
|
|
|
fprintf(stderr, "%s[%02d/%02d/%02d %02d:%02d:%02d - %s]\033[m ", color, tm_info->tm_mday, tm_info->tm_mon + 1, (tm_info->tm_year + 1900) % 100, tm_info->tm_hour, tm_info->tm_min, tm_info->tm_sec, level_str);
|
2024-02-17 00:40:09 +01:00
|
|
|
vfprintf(stderr, format, args);
|
2024-02-15 19:42:22 +01:00
|
|
|
fprintf(stderr, "\n");
|
|
|
|
|
|
|
|
// Flush the output
|
|
|
|
fflush(stderr);
|
|
|
|
|
|
|
|
va_end(args);
|
|
|
|
}
|
|
|
|
|
2024-02-17 23:59:38 +01:00
|
|
|
Status Format(char** _string, const char* fmt, ...) {
|
|
|
|
*_string = NULL;
|
2024-02-15 19:42:22 +01:00
|
|
|
|
|
|
|
va_list args;
|
2024-02-17 00:40:09 +01:00
|
|
|
va_start(args, fmt);
|
2024-02-15 19:42:22 +01:00
|
|
|
|
2024-02-17 00:40:09 +01:00
|
|
|
// Calculate the length of the formatted string
|
|
|
|
size_t length = vsnprintf(NULL, 0, fmt, args) + 1;
|
2024-02-15 19:42:22 +01:00
|
|
|
va_end(args);
|
2024-02-17 00:40:09 +01:00
|
|
|
if (length <= 0) {
|
2024-02-17 23:59:38 +01:00
|
|
|
Log(LOG_LEVEL_ERROR, "Failed to calculate the length of the formatted string.");
|
2024-02-17 00:40:09 +01:00
|
|
|
return FAILURE;
|
2024-02-15 19:42:22 +01:00
|
|
|
}
|
|
|
|
|
2024-02-17 00:40:09 +01:00
|
|
|
// Allocate a buffer for the formatted string
|
|
|
|
char* buffer = calloc(length, sizeof(char));
|
|
|
|
if (buffer == NULL) {
|
2024-02-17 23:59:38 +01:00
|
|
|
Log(LOG_LEVEL_ERROR, "Failed to allocate memory for the formatted string (%s).", strerror(errno));
|
|
|
|
return FAILURE;
|
2024-02-15 19:42:22 +01:00
|
|
|
}
|
|
|
|
|
2024-02-17 00:40:09 +01:00
|
|
|
va_start(args, fmt);
|
2024-02-15 19:42:22 +01:00
|
|
|
|
2024-02-17 00:40:09 +01:00
|
|
|
vsnprintf(buffer, length, fmt, args);
|
2024-02-15 19:42:22 +01:00
|
|
|
|
|
|
|
va_end(args);
|
|
|
|
|
2024-02-17 23:59:38 +01:00
|
|
|
*_string = buffer;
|
2024-02-17 00:40:09 +01:00
|
|
|
return SUCCESS;
|
2024-02-15 19:42:22 +01:00
|
|
|
}
|
2024-02-17 01:20:20 +01:00
|
|
|
|
2024-02-17 23:59:38 +01:00
|
|
|
Status RunExecutable(int* _exit_code, char** _stdout, char** _stderr, const char* executable, ...) {
|
|
|
|
if (_exit_code != NULL)
|
|
|
|
*_exit_code = -1;
|
|
|
|
if (_stdout != NULL)
|
|
|
|
*_stdout = NULL;
|
|
|
|
if (_stderr != NULL)
|
|
|
|
*_stderr = NULL;
|
2024-02-17 01:20:20 +01:00
|
|
|
|
|
|
|
// Count the number of arguments
|
|
|
|
int argc = 1; // The first argument is the executable itself
|
|
|
|
|
|
|
|
va_list args;
|
|
|
|
va_start(args, executable);
|
|
|
|
|
|
|
|
while (va_arg(args, const char*) != NULL)
|
|
|
|
argc++;
|
|
|
|
|
|
|
|
va_end(args);
|
|
|
|
|
|
|
|
// Allocate an array for the arguments
|
2024-02-17 23:59:38 +01:00
|
|
|
char** argv = calloc(argc + 1, sizeof(char*)); // +1 for the NULL terminator
|
2024-02-17 01:20:20 +01:00
|
|
|
if (argv == NULL) {
|
2024-02-17 23:59:38 +01:00
|
|
|
Log(LOG_LEVEL_ERROR, "Failed to allocate memory for the arguments array (%s).", strerror(errno));
|
|
|
|
return FAILURE;
|
2024-02-17 01:20:20 +01:00
|
|
|
}
|
|
|
|
|
2024-02-17 23:59:38 +01:00
|
|
|
// Fill the arguments array
|
2024-02-17 01:20:20 +01:00
|
|
|
argv[0] = strdup(executable);
|
|
|
|
if (argv[0] == NULL) {
|
2024-02-17 23:59:38 +01:00
|
|
|
Log(LOG_LEVEL_ERROR, "Failed to duplicate the executable path (%s).", strerror(errno));
|
2024-02-17 01:20:20 +01:00
|
|
|
free(argv);
|
2024-02-17 23:59:38 +01:00
|
|
|
return FAILURE;
|
2024-02-17 01:20:20 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
va_start(args, executable);
|
|
|
|
|
2024-02-17 23:59:38 +01:00
|
|
|
// Duplicate the arguments and store them in the array
|
2024-02-17 01:20:20 +01:00
|
|
|
for (int i = 1; i < argc; i++) {
|
|
|
|
argv[i] = strdup(va_arg(args, const char*));
|
|
|
|
if (argv[i] == NULL) {
|
2024-02-17 23:59:38 +01:00
|
|
|
Log(LOG_LEVEL_ERROR, "Failed to duplicate the argument n°%d (%s).", i, strerror(errno));
|
2024-02-17 01:20:20 +01:00
|
|
|
|
|
|
|
for (int j = 0; j < i; j++)
|
|
|
|
free(argv[j]);
|
|
|
|
free(argv);
|
|
|
|
|
2024-02-17 23:59:38 +01:00
|
|
|
return FAILURE;
|
2024-02-17 01:20:20 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
va_end(args);
|
|
|
|
|
|
|
|
argv[argc] = NULL;
|
|
|
|
|
2024-02-17 23:59:38 +01:00
|
|
|
// Create pipes for the standard output and error
|
2024-02-17 01:20:20 +01:00
|
|
|
int stdout_pipe[2];
|
|
|
|
if (pipe(stdout_pipe) == -1) {
|
2024-02-17 23:59:38 +01:00
|
|
|
Log(LOG_LEVEL_ERROR, "Failed to create the standard output pipe (%s).", strerror(errno));
|
2024-02-17 01:20:20 +01:00
|
|
|
|
|
|
|
for (int i = 0; i < argc; i++)
|
|
|
|
free(argv[i]);
|
|
|
|
free(argv);
|
|
|
|
|
|
|
|
return FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
int stderr_pipe[2];
|
|
|
|
if (pipe(stderr_pipe) == -1) {
|
2024-02-17 23:59:38 +01:00
|
|
|
Log(LOG_LEVEL_ERROR, "Failed to create the standard error pipe (%s).", strerror(errno));
|
2024-02-17 01:20:20 +01:00
|
|
|
|
|
|
|
for (int i = 0; i < argc; i++)
|
|
|
|
free(argv[i]);
|
|
|
|
free(argv);
|
|
|
|
|
|
|
|
close(stdout_pipe[0]);
|
|
|
|
close(stdout_pipe[1]);
|
|
|
|
|
|
|
|
return FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Fork the process
|
|
|
|
pid_t pid = fork();
|
|
|
|
|
|
|
|
if (pid == -1) {
|
2024-02-17 23:59:38 +01:00
|
|
|
Log(LOG_LEVEL_ERROR, "Failed to fork the process (%s).", strerror(errno));
|
2024-02-17 01:20:20 +01:00
|
|
|
|
|
|
|
for (int i = 0; i < argc; i++)
|
|
|
|
free(argv[i]);
|
|
|
|
free(argv);
|
|
|
|
|
|
|
|
close(stdout_pipe[0]);
|
|
|
|
close(stdout_pipe[1]);
|
|
|
|
close(stderr_pipe[0]);
|
|
|
|
close(stderr_pipe[1]);
|
|
|
|
|
|
|
|
return FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pid == 0) {
|
2024-02-17 23:59:38 +01:00
|
|
|
// Redirect the standard output and error to the pipes
|
2024-02-17 01:20:20 +01:00
|
|
|
dup2(stdout_pipe[1], STDOUT_FILENO);
|
|
|
|
dup2(stderr_pipe[1], STDERR_FILENO);
|
|
|
|
|
|
|
|
// Close the unused ends of the pipes
|
|
|
|
close(stdout_pipe[0]);
|
|
|
|
close(stdout_pipe[1]);
|
|
|
|
close(stderr_pipe[0]);
|
|
|
|
close(stderr_pipe[1]);
|
|
|
|
|
|
|
|
// Execute the command
|
2024-02-17 23:59:38 +01:00
|
|
|
execv(executable, argv);
|
2024-02-17 01:20:20 +01:00
|
|
|
|
2024-02-17 23:59:38 +01:00
|
|
|
// If execv returns, it failed
|
|
|
|
fprintf(stderr, "Failed to execute the command (%s).\n", strerror(errno));
|
2024-02-17 01:20:20 +01:00
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Close the unused ends of the pipes
|
|
|
|
close(stdout_pipe[1]);
|
|
|
|
close(stderr_pipe[1]);
|
|
|
|
|
2024-02-17 23:59:38 +01:00
|
|
|
// Free the arguments
|
|
|
|
for (int i = 0; i < argc; i++)
|
|
|
|
free(argv[i]);
|
|
|
|
free(argv);
|
|
|
|
|
|
|
|
// Wait for the child process to terminate
|
2024-02-17 01:20:20 +01:00
|
|
|
int status;
|
|
|
|
waitpid(pid, &status, 0);
|
|
|
|
|
2024-02-17 23:59:38 +01:00
|
|
|
// Read the standard output and error
|
|
|
|
if (_stdout != NULL)
|
|
|
|
ReadFileDescriptor(stdout_pipe[0], _stdout);
|
|
|
|
|
|
|
|
if (_stderr != NULL)
|
|
|
|
ReadFileDescriptor(stderr_pipe[0], _stderr);
|
2024-02-17 01:20:20 +01:00
|
|
|
|
|
|
|
// Close the pipes
|
|
|
|
close(stdout_pipe[0]);
|
|
|
|
close(stderr_pipe[0]);
|
|
|
|
|
|
|
|
// Set the exit code
|
2024-02-17 23:59:38 +01:00
|
|
|
if (_exit_code != NULL)
|
|
|
|
*_exit_code = WIFEXITED(status) ? WEXITSTATUS(status) : -1;
|
2024-02-17 01:20:20 +01:00
|
|
|
|
|
|
|
return SUCCESS;
|
|
|
|
}
|
|
|
|
|
2024-02-17 23:59:38 +01:00
|
|
|
Status ReadFileDescriptor(int fd, char** _content) {
|
|
|
|
*_content = NULL;
|
2024-02-17 01:20:20 +01:00
|
|
|
|
2024-02-17 23:59:38 +01:00
|
|
|
// Allocate a buffer for the content
|
2024-02-17 01:20:20 +01:00
|
|
|
char buffer[4096];
|
|
|
|
size_t length = 0;
|
|
|
|
|
|
|
|
ssize_t bytes_read;
|
|
|
|
while ((bytes_read = read(fd, buffer, sizeof(buffer))) > 0) {
|
2024-02-17 23:59:38 +01:00
|
|
|
char* new_content = realloc(*_content, length + bytes_read + 1); // +1 for the NULL terminator
|
|
|
|
if (new_content == NULL) {
|
|
|
|
Log(LOG_LEVEL_ERROR, "Failed to reallocate memory for the file content (%s).", strerror(errno));
|
|
|
|
free(*_content);
|
|
|
|
return FAILURE;
|
2024-02-17 01:20:20 +01:00
|
|
|
}
|
2024-02-17 23:59:38 +01:00
|
|
|
*_content = new_content;
|
2024-02-17 01:20:20 +01:00
|
|
|
|
2024-02-17 23:59:38 +01:00
|
|
|
memcpy(*_content + length, buffer, bytes_read);
|
2024-02-17 01:20:20 +01:00
|
|
|
length += bytes_read;
|
2024-02-17 23:59:38 +01:00
|
|
|
|
|
|
|
(*_content)[length] = '\0';
|
2024-02-17 01:20:20 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (bytes_read == -1) {
|
2024-02-17 23:59:38 +01:00
|
|
|
Log(LOG_LEVEL_ERROR, "Failed to read from the file descriptor (%s).", strerror(errno));
|
|
|
|
free(*_content);
|
2024-02-17 01:20:20 +01:00
|
|
|
return FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return SUCCESS;
|
|
|
|
}
|
2024-02-17 12:34:24 +01:00
|
|
|
|
2024-02-17 23:59:38 +01:00
|
|
|
Status WriteFileDescriptor(int fd, const char* content) {
|
|
|
|
size_t length = strlen(content);
|
2024-02-17 12:34:24 +01:00
|
|
|
ssize_t bytes_written = 0;
|
|
|
|
|
|
|
|
while (bytes_written < length) {
|
2024-02-17 23:59:38 +01:00
|
|
|
ssize_t result = write(fd, content + bytes_written, length - bytes_written);
|
2024-02-17 12:34:24 +01:00
|
|
|
if (result == -1) {
|
2024-02-17 23:59:38 +01:00
|
|
|
Log(LOG_LEVEL_ERROR, "Failed to write to the file descriptor (%s).", strerror(errno));
|
2024-02-17 12:34:24 +01:00
|
|
|
return FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
bytes_written += result;
|
|
|
|
}
|
|
|
|
|
|
|
|
return SUCCESS;
|
|
|
|
}
|
|
|
|
|
2024-02-17 23:59:38 +01:00
|
|
|
Status ReadFile(const char* path, char** _content) {
|
|
|
|
*_content = NULL;
|
|
|
|
|
2024-02-17 12:34:24 +01:00
|
|
|
// Open the file
|
2024-02-17 23:59:38 +01:00
|
|
|
int file = open(path, O_RDONLY);
|
|
|
|
if (file == -1) {
|
|
|
|
Log(LOG_LEVEL_ERROR, "Failed to open the file %s (%s).", path, strerror(errno));
|
2024-02-17 12:34:24 +01:00
|
|
|
return FAILURE;
|
|
|
|
}
|
|
|
|
|
2024-02-17 23:59:38 +01:00
|
|
|
// Read the file
|
|
|
|
Status status = ReadFileDescriptor(file, _content);
|
2024-02-17 12:34:24 +01:00
|
|
|
|
|
|
|
// Close the file
|
2024-02-17 23:59:38 +01:00
|
|
|
close(file);
|
2024-02-17 12:34:24 +01:00
|
|
|
|
2024-02-17 23:59:38 +01:00
|
|
|
return status;
|
2024-02-17 12:34:24 +01:00
|
|
|
}
|
2024-02-17 12:55:21 +01:00
|
|
|
|
2024-02-17 23:59:38 +01:00
|
|
|
Status WriteFile(const char* path, const char* content) {
|
|
|
|
// Open the file
|
|
|
|
int file = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
|
|
|
if (file == -1) {
|
|
|
|
Log(LOG_LEVEL_ERROR, "Failed to open the file %s (%s).", path, strerror(errno));
|
2024-02-17 14:21:14 +01:00
|
|
|
return FAILURE;
|
|
|
|
}
|
|
|
|
|
2024-02-17 23:59:38 +01:00
|
|
|
// Write the content
|
|
|
|
Status status = WriteFileDescriptor(file, content);
|
2024-02-17 14:21:14 +01:00
|
|
|
|
2024-02-17 23:59:38 +01:00
|
|
|
// Close the file
|
|
|
|
close(file);
|
2024-02-17 14:38:52 +01:00
|
|
|
|
2024-02-17 23:59:38 +01:00
|
|
|
return status;
|
2024-02-17 14:38:52 +01:00
|
|
|
}
|