#include "utils.h" #include #include #include #include #include #include #include #include #include #include LogLevel log_level = LOG_LEVEL_WARNING; void set_log_level(LogLevel level) { log_level = level; } void log_message(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_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); } Result format(char** out_string, const char* fmt, ...) { *out_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_message(LOG_LEVEL_ERROR, "Failed to calculate the length of the formatted string (%s).", strerror(errno)); return FAILURE; } // Allocate a buffer for the formatted string char* buffer = calloc(length, sizeof(char)); if (buffer == NULL) { log_message(LOG_LEVEL_ERROR, "Failed to allocate memory for the formatted string (%s).", strerror(errno)); return OUT_OF_MEMORY; } va_start(args, fmt); // Format the string vsnprintf(buffer, length, fmt, args); va_end(args); *out_string = buffer; return SUCCESS; } Result run_executable(int* out_exit_code, char** out_stdout, char** out_stderr, const char* executable, ...) { if (out_exit_code != NULL) *out_exit_code = -1; if (out_stdout != NULL) *out_stdout = NULL; if (out_stderr != NULL) *out_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*)); if (argv == NULL) { log_message(LOG_LEVEL_ERROR, "Failed to allocate memory for the arguments (%s).", strerror(errno)); return OUT_OF_MEMORY; } // Fill the array with the arguments argv[0] = strdup(executable); if (argv[0] == NULL) { log_message(LOG_LEVEL_ERROR, "Failed to duplicate the executable string (%s).", strerror(errno)); free(argv); return OUT_OF_MEMORY; } va_start(args, executable); for (int i = 1; i < argc; i++) { argv[i] = strdup(va_arg(args, const char*)); if (argv[i] == NULL) { log_message(LOG_LEVEL_ERROR, "Failed to duplicate the argument string (%s).", strerror(errno)); for (int j = 0; j < i; j++) free(argv[j]); free(argv); va_end(args); return OUT_OF_MEMORY; } } va_end(args); // Set the last element of the array to NULL argv[argc] = NULL; // Create pipes for the stdout and stderr int stdout_pipe[2]; if (pipe(stdout_pipe) == -1) { log_message(LOG_LEVEL_ERROR, "Failed to create the stdout 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_message(LOG_LEVEL_ERROR, "Failed to create the stderr 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_message(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 stdout and stderr 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 execvp(executable, argv); // If the command failed to execute, print an error message and exit fprintf(stderr, "Failed to execute the command '%s' (%s).\n", executable, strerror(errno)); exit(EXIT_FAILURE); } // Free the arguments array for (int i = 0; i < argc; i++) free(argv[i]); free(argv); // Close the unused ends of the pipes close(stdout_pipe[1]); close(stderr_pipe[1]); // Wait for the child process to finish int status; waitpid(pid, &status, 0); // Read the output from the pipes if (out_stdout != NULL) read_fd(stdout_pipe[0], out_stdout); if (out_stderr != NULL) read_fd(stderr_pipe[0], out_stderr); // Close the pipes close(stdout_pipe[0]); close(stderr_pipe[0]); // Set the exit code if (out_exit_code != NULL) *out_exit_code = WEXITSTATUS(status); return SUCCESS; } Result read_fd(int fd, char** out_string) { *out_string = NULL; // Create a buffer for the output char buffer[4096]; size_t length = 0; // Read the output from the file descriptor ssize_t bytes_read; while ((bytes_read = read(fd, buffer, sizeof(buffer))) > 0) { // Reallocate the buffer char* new_buffer = realloc(*out_string, length + bytes_read + 1); if (new_buffer == NULL) { log_message(LOG_LEVEL_ERROR, "Failed to reallocate memory for the output string (%s).", strerror(errno)); free(*out_string); *out_string = NULL; return OUT_OF_MEMORY; } *out_string = new_buffer; // Copy the new data to the buffer memcpy(*out_string + length, buffer, bytes_read); length += bytes_read; } // Check if an error occurred if (bytes_read == -1) { log_message(LOG_LEVEL_ERROR, "Failed to read from the file descriptor (%s).", strerror(errno)); free(*out_string); *out_string = NULL; return FAILURE; } // Null-terminate the string if (length > 0) (*out_string)[length] = '\0'; return SUCCESS; } Result read_file(const char* path, char** out_string) { *out_string = NULL; // Open the file int fd = open(path, O_RDONLY); if (fd == -1) { log_message(LOG_LEVEL_ERROR, "Failed to open the file '%s' (%s).", path, strerror(errno)); return FAILURE; } // Read the file Result result = read_fd(fd, out_string); // Close the file close(fd); return result; } Result write_fd(int fd, const char* string) { // Write the string to the file descriptor size_t length = strlen(string); ssize_t bytes_written = 0; while (bytes_written < length) { ssize_t result = write(fd, string + bytes_written, length - bytes_written); if (result == -1) { log_message(LOG_LEVEL_ERROR, "Failed to write to the file descriptor (%s).", strerror(errno)); return FAILURE; } bytes_written += result; } return SUCCESS; } Result write_file(const char* path, const char* string) { // Open the file int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644); if (fd == -1) { log_message(LOG_LEVEL_ERROR, "Failed to open the file '%s' (%s).", path, strerror(errno)); return FAILURE; } // Write the string to the file Result result = write_fd(fd, string); // Close the file close(fd); return result; } Result copy_file(const char* src_path, const char* dst_path, mode_t mode) { // Open the source file int src_fd = open(src_path, O_RDONLY); if (src_fd == -1) { log_message(LOG_LEVEL_ERROR, "Failed to open the source file '%s' (%s).", src_path, strerror(errno)); return FAILURE; } // Open the destination file int dst_fd = open(dst_path, O_WRONLY | O_CREAT | O_TRUNC, mode); if (dst_fd == -1) { log_message(LOG_LEVEL_ERROR, "Failed to open the destination file '%s' (%s).", dst_path, strerror(errno)); close(src_fd); return FAILURE; } // Copy the file char buffer[4096]; ssize_t bytes_read; while ((bytes_read = read(src_fd, buffer, sizeof(buffer))) > 0) { ssize_t bytes_written = 0; while (bytes_written < bytes_read) { ssize_t result = write(dst_fd, buffer + bytes_written, bytes_read - bytes_written); if (result == -1) { log_message(LOG_LEVEL_ERROR, "Failed to write to the destination file '%s' (%s).", dst_path, strerror(errno)); close(src_fd); close(dst_fd); return FAILURE; } bytes_written += result; } } // Check if an error occurred if (bytes_read == -1) { log_message(LOG_LEVEL_ERROR, "Failed to read from the source file '%s' (%s).", src_path, strerror(errno)); close(src_fd); close(dst_fd); return FAILURE; } // Close the files close(src_fd); close(dst_fd); return SUCCESS; } Result format_size(uint64_t size, char** out_string) { *out_string = NULL; // Determine the unit const char* unit; double value; if (size < 1024ULL) { unit = "B"; value = size; } else if (size < 1024ULL * 1024) { unit = "KiB"; value = (double)size / 1024; } else if (size < 1024ULL * 1024 * 1024) { unit = "MiB"; value = (double)size / (1024 * 1024); } else if (size < 1024ULL * 1024 * 1024 * 1024) { unit = "GiB"; value = (double)size / (1024 * 1024 * 1024); } else if (size < 1024ULL * 1024 * 1024 * 1024 * 1024) { unit = "TiB"; value = (double)size / (1024 * 1024 * 1024 * 1024); } else { unit = "PiB"; value = (double)size / (1024 * 1024 * 1024 * 1024 * 1024); } // Format the string return format(out_string, "%.2f%s", value, unit); } Result parse_size(const char* string, uint64_t* out_size) { // Parse the size char* endptr; uint64_t size = strtoull(string, &endptr, 10); if (endptr == string) { log_message(LOG_LEVEL_ERROR, "Failed to parse the size '%s'.", string); return FAILURE; } // Determine the unit if (*endptr == '\0') { // No unit *out_size = size; } else if (strcmp(endptr, "K") == 0 || strcmp(endptr, "KB") == 0 || strcmp(endptr, "KiB") == 0) { // Kilobytes *out_size = size * 1024; } else if (strcmp(endptr, "M") == 0 || strcmp(endptr, "MB") == 0 || strcmp(endptr, "MiB") == 0) { // Megabytes *out_size = size * 1024 * 1024; } else if (strcmp(endptr, "G") == 0 || strcmp(endptr, "GB") == 0 || strcmp(endptr, "GiB") == 0) { // Gigabytes *out_size = size * 1024 * 1024 * 1024; } else if (strcmp(endptr, "T") == 0 || strcmp(endptr, "TB") == 0 || strcmp(endptr, "TiB") == 0) { // Terabytes *out_size = size * 1024 * 1024 * 1024 * 1024; } else if (strcmp(endptr, "P") == 0 || strcmp(endptr, "PB") == 0 || strcmp(endptr, "PiB") == 0) { // Petabytes *out_size = size * 1024 * 1024 * 1024 * 1024 * 1024; } else { log_message(LOG_LEVEL_ERROR, "Unknown unit '%s'.", endptr); return FAILURE; } return SUCCESS; }