Files
linuxinstall/src/utils.c

290 lines
6.3 KiB
C
Raw Normal View History

2024-02-15 19:42:22 +01:00
#include "utils.h"
#include <stdarg.h>
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
2024-02-15 19:42:22 +01:00
#include <string.h>
#include <errno.h>
2024-02-17 01:20:20 +01:00
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
2024-02-15 19:42:22 +01:00
LogLevel log_level = LOG_LEVEL_WARNING;
2024-02-15 19:42:22 +01:00
void set_log_level(LogLevel level) {
log_level = level;
2024-02-15 19:42:22 +01:00
}
void log_message(LogLevel level, const char* format, ...) {
if (level < log_level)
return;
2024-02-15 19:42:22 +01:00
va_list args;
va_start(args, format);
2024-02-15 19:42:22 +01:00
const char* color;
const char* level_str;
// Set the color and level_str based on the log level
2024-02-15 19:42:22 +01:00
switch (level) {
case LOG_LEVEL_DEBUG:
2024-02-15 19:42:22 +01:00
color = "\033[0;90m";
level_str = "DEBUG";
break;
case LOG_LEVEL_WARNING:
2024-02-15 19:42:22 +01:00
color = "\033[0;33m";
level_str = "WARNING";
2024-02-15 19:42:22 +01:00
break;
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);
// 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);
vfprintf(stderr, format, args);
2024-02-15 19:42:22 +01:00
fprintf(stderr, "\n");
// Flush the output
fflush(stderr);
va_end(args);
}
Result format(char** out_string, const char* fmt, ...) {
*out_string = NULL;
2024-02-15 19:42:22 +01:00
va_list args;
va_start(args, fmt);
2024-02-15 19:42:22 +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);
if (length <= 0) {
log_message(LOG_LEVEL_ERROR, "Failed to calculate the length of the formatted string (%s).", strerror(errno));
return FAILURE;
2024-02-15 19:42:22 +01:00
}
// 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;
2024-02-15 19:42:22 +01:00
}
va_start(args, fmt);
2024-02-15 19:42:22 +01:00
// Format the string
vsnprintf(buffer, length, fmt, args);
2024-02-15 19:42:22 +01:00
va_end(args);
*out_string = buffer;
return SUCCESS;
2024-02-15 19:42:22 +01:00
}
2024-02-17 01:20:20 +01:00
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;
}