#include "utils.h" #include #include #include #include #include #include #include #include #include #include LogLevel log_level = LOG_LEVEL_INFO; void SetLogLevel(LogLevel level) { log_level = level; } void Log(LogLevel level, const char* format, ...) { if (level < log_level) return; va_list args; va_start(args, format); const char* color; const char* level_str; // Set the color and level_str based on the log level switch (level) { case LOG_LEVEL_DEBUG: color = "\033[0;90m"; level_str = "DEBUG"; break; case LOG_LEVEL_INFO: color = "\033[0;36m"; level_str = "INFO"; break; case LOG_LEVEL_WARNING: color = "\033[0;33m"; level_str = "WARNING"; break; case LOG_LEVEL_ERROR: color = "\033[0;31m"; level_str = "ERROR"; break; } // Get the current time time_t t = time(NULL); struct tm* tm_info = localtime(&t); // Print the label, message and newline 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); vfprintf(stderr, format, args); fprintf(stderr, "\n"); // Flush the output fflush(stderr); va_end(args); } Status Format(char** _string, const char* fmt, ...) { *_string = NULL; va_list args; va_start(args, fmt); // Calculate the length of the formatted string size_t length = vsnprintf(NULL, 0, fmt, args) + 1; va_end(args); if (length <= 0) { Log(LOG_LEVEL_ERROR, "Failed to calculate the length of the formatted string."); return FAILURE; } // Allocate a buffer for the formatted string char* buffer = calloc(length, sizeof(char)); if (buffer == NULL) { Log(LOG_LEVEL_ERROR, "Failed to allocate memory for the formatted string (%s).", strerror(errno)); return FAILURE; } va_start(args, fmt); vsnprintf(buffer, length, fmt, args); va_end(args); *_string = buffer; return SUCCESS; } 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; // 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 char** argv = calloc(argc + 1, sizeof(char*)); // +1 for the NULL terminator if (argv == NULL) { Log(LOG_LEVEL_ERROR, "Failed to allocate memory for the arguments array (%s).", strerror(errno)); return FAILURE; } // Fill the arguments array argv[0] = strdup(executable); if (argv[0] == NULL) { Log(LOG_LEVEL_ERROR, "Failed to duplicate the executable path (%s).", strerror(errno)); free(argv); return FAILURE; } va_start(args, executable); // Duplicate the arguments and store them in the array for (int i = 1; i < argc; i++) { argv[i] = strdup(va_arg(args, const char*)); if (argv[i] == NULL) { Log(LOG_LEVEL_ERROR, "Failed to duplicate the argument n°%d (%s).", i, strerror(errno)); for (int j = 0; j < i; j++) free(argv[j]); free(argv); return FAILURE; } } va_end(args); argv[argc] = NULL; // Create pipes for the standard output and error int stdout_pipe[2]; if (pipe(stdout_pipe) == -1) { Log(LOG_LEVEL_ERROR, "Failed to create the standard output pipe (%s).", strerror(errno)); for (int i = 0; i < argc; i++) free(argv[i]); free(argv); return FAILURE; } int stderr_pipe[2]; if (pipe(stderr_pipe) == -1) { Log(LOG_LEVEL_ERROR, "Failed to create the standard error pipe (%s).", strerror(errno)); 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) { Log(LOG_LEVEL_ERROR, "Failed to fork the process (%s).", strerror(errno)); 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) { // Redirect the standard output and error to the pipes 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 execv(executable, argv); // If execv returns, it failed fprintf(stderr, "Failed to execute the command (%s).\n", strerror(errno)); exit(EXIT_FAILURE); } // Close the unused ends of the pipes close(stdout_pipe[1]); close(stderr_pipe[1]); // Free the arguments for (int i = 0; i < argc; i++) free(argv[i]); free(argv); // Wait for the child process to terminate int status; waitpid(pid, &status, 0); // Read the standard output and error if (_stdout != NULL) ReadFileDescriptor(stdout_pipe[0], _stdout); if (_stderr != NULL) ReadFileDescriptor(stderr_pipe[0], _stderr); // Close the pipes close(stdout_pipe[0]); close(stderr_pipe[0]); // Set the exit code if (_exit_code != NULL) *_exit_code = WIFEXITED(status) ? WEXITSTATUS(status) : -1; return SUCCESS; } Status ReadFileDescriptor(int fd, char** _content) { *_content = NULL; // Allocate a buffer for the content char buffer[4096]; size_t length = 0; ssize_t bytes_read; while ((bytes_read = read(fd, buffer, sizeof(buffer))) > 0) { 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; } *_content = new_content; memcpy(*_content + length, buffer, bytes_read); length += bytes_read; (*_content)[length] = '\0'; } if (bytes_read == -1) { Log(LOG_LEVEL_ERROR, "Failed to read from the file descriptor (%s).", strerror(errno)); free(*_content); return FAILURE; } return SUCCESS; } Status WriteFileDescriptor(int fd, const char* content) { size_t length = strlen(content); ssize_t bytes_written = 0; while (bytes_written < length) { ssize_t result = write(fd, content + bytes_written, length - bytes_written); if (result == -1) { Log(LOG_LEVEL_ERROR, "Failed to write to the file descriptor (%s).", strerror(errno)); return FAILURE; } bytes_written += result; } return SUCCESS; } Status ReadFile(const char* path, char** _content) { *_content = NULL; // Open the file int file = open(path, O_RDONLY); if (file == -1) { Log(LOG_LEVEL_ERROR, "Failed to open the file %s (%s).", path, strerror(errno)); return FAILURE; } // Read the file Status status = ReadFileDescriptor(file, _content); // Close the file close(file); return status; } 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)); return FAILURE; } // Write the content Status status = WriteFileDescriptor(file, content); // Close the file close(file); return status; } Status CopyFile(const char* source_path, const char* destination_path) { // Open the source file int source_file = open(source_path, O_RDONLY); if (source_file == -1) { Log(LOG_LEVEL_ERROR, "Failed to open the source file %s (%s).", source_path, strerror(errno)); return FAILURE; } // Open the destination file int destination_file = open(destination_path, O_WRONLY | O_CREAT | O_TRUNC, 0644); if (destination_file == -1) { Log(LOG_LEVEL_ERROR, "Failed to open the destination file %s (%s).", destination_path, strerror(errno)); close(source_file); return FAILURE; } // Copy the file char buffer[4096]; ssize_t bytes_read; while ((bytes_read = read(source_file, buffer, sizeof(buffer))) > 0) { ssize_t bytes_written = 0; while (bytes_written < bytes_read) { ssize_t result = write(destination_file, buffer + bytes_written, bytes_read - bytes_written); if (result == -1) { Log(LOG_LEVEL_ERROR, "Failed to write to the destination file %s (%s).", destination_path, strerror(errno)); close(source_file); close(destination_file); return FAILURE; } bytes_written += result; } } if (bytes_read == -1) { Log(LOG_LEVEL_ERROR, "Failed to read from the source file %s (%s).", source_path, strerror(errno)); close(source_file); close(destination_file); return FAILURE; } // Close the files close(source_file); close(destination_file); return SUCCESS; }