Files
linuxinstall/src/utils.c

408 lines
10 KiB
C
Raw Normal View History

#include "utils.h"
#include <string.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <dirent.h>
2024-02-15 12:50:30 +01:00
#include <errno.h>
#include <sys/wait.h>
#include <sys/types.h>
2024-02-15 00:44:40 +01:00
#include <sys/stat.h>
char* format(const char* fmt, ...) {
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)
return NULL;
// Allocate a buffer for the formatted string
char* buffer = calloc(length, sizeof(char));
if (buffer == NULL) {
log_msg(LOG_ERROR, "Failed to allocate memory for the formatted string.");
return NULL;
}
va_start(args, fmt);
// Format the string
vsnprintf(buffer, length, fmt, args);
va_end(args);
return buffer;
}
void log_msg(LogLevel level, const char* fmt, ...) {
va_list args;
va_start(args, fmt);
const char* color;
const char* level_str;
// Set the color and level string based on the log level
switch (level) {
case LOG_DEBUG:
color = "\033[0;90m";
level_str = "DEBUG";
break;
case LOG_INFO:
color = "\033[0;34m";
level_str = "INFO";
break;
case LOG_WARN:
color = "\033[0;33m";
level_str = "WARN";
break;
case LOG_ERROR:
color = "\033[0;31m";
level_str = "ERROR";
break;
default:
color = "";
level_str = "UNKNOWN";
break;
}
// Get the current time
time_t t = time(NULL);
struct tm* tm_info = localtime(&t);
// Print the label
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);
// Print the message
vfprintf(stderr, fmt, args);
// Print a newline
fprintf(stderr, "\n");
// Flush the output
fflush(stderr);
va_end(args);
}
int execute(char** stdout_buffer, char** stderr_buffer, const char* file, ...) {
// Count the number of arguments
int argc = 1; // Include space for the file name
va_list args;
va_start(args, file);
while (va_arg(args, char*) != NULL)
argc++;
va_end(args);
// Allocate an array for the arguments
char** argv = calloc(argc + 1, sizeof(char*)); // Include space for the NULL terminator
if (argv == NULL) {
log_msg(LOG_ERROR, "Failed to allocate memory for the argument array.");
return -1;
}
// Fill the argument array
va_start(args, file);
argv[0] = strdup(file);
for (int i = 1; i < argc; i++)
argv[i] = strdup(va_arg(args, char*));
argv[argc] = NULL;
va_end(args);
// Create the stdout pipe for the child process
int stdout_pipe[2];
if (pipe(stdout_pipe) == -1) {
log_msg(LOG_ERROR, "Failed to create pipe for stdout.");
for (int i = 0; i < argc; i++)
free(argv[i]);
free(argv);
return -1;
}
// Create the stderr pipe for the child process
int stderr_pipe[2];
if (pipe(stderr_pipe) == -1) {
log_msg(LOG_ERROR, "Failed to create pipe for stderr.");
close(stdout_pipe[0]);
close(stdout_pipe[1]);
for (int i = 0; i < argc; i++)
free(argv[i]);
free(argv);
return -1;
}
// Fork the child process
pid_t pid = fork();
if (pid == -1) {
log_msg(LOG_ERROR, "Failed to fork child process.");
close(stdout_pipe[0]);
close(stdout_pipe[1]);
close(stderr_pipe[0]);
close(stderr_pipe[1]);
for (int i = 0; i < argc; i++)
free(argv[i]);
free(argv);
return -1;
}
if (pid == 0) {
// Redirect stdout and stderr to the pipes
dup2(stdout_pipe[1], STDOUT_FILENO);
dup2(stderr_pipe[1], STDERR_FILENO);
// Close the pipe file descriptors
close(stdout_pipe[0]);
close(stdout_pipe[1]);
close(stderr_pipe[0]);
close(stderr_pipe[1]);
// Execute the command
execvp(file, argv);
// If execvp fails, log an error
log_msg(LOG_ERROR, "Failed to execute command '%s'.", file);
// Free the argument array
for (int i = 0; i < argc; i++)
free(argv[i]);
free(argv);
exit(1);
}
// Close the write end 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 stdout buffer
char buffer[4096];
ssize_t bytes_read;
if (stdout_buffer != NULL) {
size_t stdout_length = 0;
*stdout_buffer = NULL;
// Read the stdout buffer
while ((bytes_read = read(stdout_pipe[0], buffer, sizeof(buffer))) > 0) {
char* new_stdout_buffer = realloc(*stdout_buffer, stdout_length + bytes_read + 1); // Include space for the null terminator
if (new_stdout_buffer == NULL) {
log_msg(LOG_ERROR, "Failed to allocate memory for the stdout buffer.");
free(*stdout_buffer);
close(stdout_pipe[0]);
close(stderr_pipe[0]);
for (int i = 0; i < argc; i++)
free(argv[i]);
free(argv);
return -1;
}
*stdout_buffer = new_stdout_buffer;
memcpy(*stdout_buffer + stdout_length, buffer, bytes_read);
stdout_length += bytes_read;
(*stdout_buffer)[stdout_length] = '\0';
}
}
// Close the read end of the stdout pipe
close(stdout_pipe[0]);
// Read the stderr buffer
if (stderr_buffer != NULL) {
size_t stderr_length = 0;
*stderr_buffer = NULL;
// Read the stderr buffer
while ((bytes_read = read(stderr_pipe[0], buffer, sizeof(buffer))) > 0) {
char* new_stderr_buffer = realloc(*stderr_buffer, stderr_length + bytes_read + 1); // Include space for the null terminator
if (new_stderr_buffer == NULL) {
log_msg(LOG_ERROR, "Failed to allocate memory for the stderr buffer.");
free(*stdout_buffer);
free(*stderr_buffer);
close(stderr_pipe[0]);
for (int i = 0; i < argc; i++)
free(argv[i]);
free(argv);
return -1;
}
*stderr_buffer = new_stderr_buffer;
memcpy(*stderr_buffer + stderr_length, buffer, bytes_read);
stderr_length += bytes_read;
(*stderr_buffer)[stderr_length] = '\0';
}
}
// Close the read end of the stderr pipe
close(stderr_pipe[0]);
// Free the argument array
for (int i = 0; i < argc; i++)
free(argv[i]);
free(argv);
// Return the exit status
return WIFEXITED(status) ? WEXITSTATUS(status) : -1;
}
2024-02-15 12:50:30 +01:00
bool create_directory(const char* directory, mode_t mode, uid_t uid, gid_t gid) {
// Create the directory
if (mkdir(directory, mode) != 0) {
log_msg(LOG_ERROR, "Failed to create directory '%s' (%s).", directory, strerror(errno));
return false;
}
// Change the owner of the directory
if (chown(directory, uid, gid) != 0) {
log_msg(LOG_ERROR, "Failed to change the owner of directory '%s' (%s).", directory, strerror(errno));
return false;
}
return true;
}
bool delete_directory(const char* directory, int level) {
if (level > MAX_RECURSION_LEVEL) {
log_msg(LOG_ERROR, "Too many levels of recursion when deleting directory '%s'.", directory);
return false;
}
2024-02-15 00:44:40 +01:00
// Open the directory
DIR* dir = opendir(directory);
if (dir == NULL) {
2024-02-15 12:50:30 +01:00
log_msg(LOG_ERROR, "Failed to open directory '%s' (%s).", directory, strerror(errno));
2024-02-15 00:44:40 +01:00
return false;
}
// Iterate over the directory entries
struct dirent* entry;
while ((entry = readdir(dir)) != NULL) {
// Skip . and ..
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
continue;
// Combine the directory path with the entry name
char* entry_path = format("%s/%s", directory, entry->d_name);
if (entry_path == NULL) {
log_msg(LOG_ERROR, "Failed to allocate memory for the entry path.");
closedir(dir);
return false;
}
// Check if the entry is a directory
struct stat statbuf;
int result = stat(entry_path, &statbuf);
if (result != 0) {
2024-02-15 12:50:30 +01:00
log_msg(LOG_ERROR, "Failed to get information about file '%s' (%s).", entry_path, strerror(errno));
2024-02-15 00:44:40 +01:00
free(entry_path);
closedir(dir);
return false;
}
if (S_ISDIR(statbuf.st_mode)) {
// Delete the directory
2024-02-15 12:50:30 +01:00
if (!delete_directory(entry_path, level + 1)) {
log_msg(LOG_ERROR, "Failed to delete directory '%s'.", entry_path);
2024-02-15 00:44:40 +01:00
free(entry_path);
closedir(dir);
return false;
}
} else {
// Delete the file
if (unlink(entry_path) != 0) {
2024-02-15 12:50:30 +01:00
log_msg(LOG_ERROR, "Failed to delete file '%s' (%s).", entry_path, strerror(errno));
2024-02-15 00:44:40 +01:00
free(entry_path);
closedir(dir);
return false;
}
}
free(entry_path);
}
// Close the directory
closedir(dir);
// Delete the directory
if (rmdir(directory) != 0) {
2024-02-15 12:50:30 +01:00
log_msg(LOG_ERROR, "Failed to delete directory '%s' (%s).", directory, strerror(errno));
2024-02-15 00:44:40 +01:00
return false;
}
return true;
}
bool copy_file(const char* source, const char* destination, mode_t mode, uid_t uid, gid_t gid) {
// Open the source file
FILE* source_file = fopen(source, "r");
if (source_file == NULL) {
log_msg(LOG_ERROR, "Failed to open file '%s' for reading (%s).", source, strerror(errno));
return false;
}
// Open the destination file
FILE* destination_file = fopen(destination, "w");
if (destination_file == NULL) {
log_msg(LOG_ERROR, "Failed to open file '%s' for writing (%s).", destination, strerror(errno));
fclose(source_file);
return false;
}
// Copy the file
char buffer[4096];
size_t bytes_read;
while ((bytes_read = fread(buffer, 1, sizeof(buffer), source_file)) > 0)
if (fwrite(buffer, 1, bytes_read, destination_file) != bytes_read) {
log_msg(LOG_ERROR, "Failed to write to file '%s' (%s).", destination, strerror(errno));
fclose(source_file);
fclose(destination_file);
return false;
}
// Close the files
fclose(source_file);
fclose(destination_file);
// Change the mode of the destination file
if (chmod(destination, mode) != 0) {
log_msg(LOG_ERROR, "Failed to change the mode of file '%s' (%s).", destination, strerror(errno));
return false;
}
// Change the owner of the destination file
if (chown(destination, uid, gid) != 0) {
log_msg(LOG_ERROR, "Failed to change the owner of file '%s' (%s).", destination, strerror(errno));
return false;
}
return true;
}